我正在使用Jersey为服务器组件创建REST web服务.

我想在列表中序列化的JAXB注释对象如下所示:

@XmlRootElement(name = "distribution")
@XmlType(name = "tDistribution", propOrder = {
    "id", "name"
})
public class XMLDistribution {
    private String id;
    private String name;
    // no-args constructor, getters, setters, etc
}

I have a REST resource to retrieve one distribution which looks like this:

@Path("/distribution/{id: [1-9][0-9]*}")
public class RESTDistribution {
    @GET
    @Produces("application/json")
    public XMLDistribution retrieve(@PathParam("id") String id) {
        return retrieveDistribution(Long.parseLong(id));
    }
    // business logic (retrieveDistribution(long))
}

我还有一个REST资源来检索所有发行版的列表,如下所示:

@Path("/distributions")
public class RESTDistributions {
    @GET
    @Produces("application/json")
    public List<XMLDistribution> retrieveAll() {
        return retrieveDistributions();
    }
    // business logic (retrieveDistributions())
}

I use a ContextResolver to customize JAXB serialization, which is currently configured like this:

@Provider
@Produces("application/json")
public class JAXBJSONContextResolver implements ContextResolver<JAXBContext> {
    private JAXBContext context;
    public JAXBJSONContextResolver() throws Exception {
        JSONConfiguration.MappedBuilder b = JSONConfiguration.mapped();
        b.nonStrings("id");
        b.rootUnwrapping(true);
        b.arrays("distribution");
        context = new JSONJAXBContext(b.build(), XMLDistribution.class);
    }
    @Override
    public JAXBContext getContext(Class<?> objectType) {
        return context;
    }
}

Both REST resources work, as well as the context resolver. This is an example of output for the first one:

// path: /distribution/1
{
  "id": 1,
  "name": "Example Distribution"
}

这正是我想要的.以下是列表的输出示例:

// path: /distributions
{
  "distribution": [{
    "id": 1,
    "name": "Sample Distribution 1"
  }, {
    "id": 2,
    "name": "Sample Distribution 2"
  }]
}

Which is not quite what I want.

我不明白为什么里面有一个distribution号标签.我想在上下文解析器中用.rootUnwrapping(true)删除它,但显然这只会删除另一个封闭标记.这是.rootUnwrapping(false)的输出:

// path: /distribution/1
{
  "distribution": {
    "id": 1,
    "name": "Example Distribution"
  }
} // not ok
// path: /distributions
{
  "xMLDistributions": {
    "distribution": [{
      "id": 1,
      "name": "Sample Distribution 1"
    }, {
      "id": 2,
      "name": "Sample Distribution 2"
    }]
  }
}

我还必须配置.arrays("distribution")以始终获得JSON数组,即使只有一个元素.

Ideally, I'd like to have this as an output:

// path: /distribution/1
{
  "id": 1,
  "name": "Example Distribution"
} // currently works
// path: /distributions
[{
  "id": 1,
  "name": "Sample Distribution 1"
}, {
  "id": 2,
  "name": "Sample Distribution 2"
}]

我试图返回一个List<XMLDistribution>、一个XMLDistributionList(围绕列表的包装)、一个XMLDistribution[],但我找不到一种方法以我所需的格式获得一个简单的JSON发行版array.

我还try 了由JSONConfiguration.natural()JSONConfiguration.mappedJettison()等返回的其他表示法,但无法得到与我需要的类似的任何表示法.

有人知道是否可以将JAXB配置为这样做吗?

推荐答案

I found a solution: replace the JAXB JSON serializer with a better behaved JSON serializer like Jackson. The easy way is to use jackson-jaxrs, which has already done it for you. The class is JacksonJsonProvider. All you have to do is edit your project's web.xml so that Jersey (or another JAX-RS implementation) scans for it. Here's what you need to add:

<init-param>
  <param-name>com.sun.jersey.config.property.packages</param-name>
  <param-value>your.project.packages;org.codehaus.jackson.jaxrs</param-value>
</init-param>

And that's all there is to it. Jackson will be used for JSON serialization, and it works the way you expect for lists and arrays.

The longer way is to write your own custom MessageBodyWriter registered to produce "application/json". Here's an example:

@Provider
@Produces("application/json")
public class JsonMessageBodyWriter implements MessageBodyWriter {
    @Override
    public long getSize(Object obj, Class type, Type genericType,
            Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    @Override
    public boolean isWriteable(Class type, Type genericType,
            Annotation annotations[], MediaType mediaType) {
        return true;
    }

    @Override
    public void writeTo(Object target, Class type, Type genericType,
            Annotation[] annotations, MediaType mediaType,
            MultivaluedMap httpHeaders, OutputStream outputStream)
            throws IOException {        
        new ObjectMapper().writeValue(outputStream, target);
    }
}

You'll need to make sure your web.xml includes the package, as for the ready-made solution above.

Either way: voila! You'll see properly formed JSON.

You can download Jackson from here: http://jackson.codehaus.org/

Json相关问答推荐

JOLT规范:转移到现有数组

JoltChange:将输入数组的每个对象拆分为2个独立的对象,并将其放入输出数组中

JSON:将项';S键/名称移动到属性中,并使用JQ将其转换为数组

ArcGIS json到Geojson的变换

在Jolt中将具有嵌套元素的对象数组转换为单独的对象列表

Ansible - 将文件内容添加到字典中

将 json 转换为 jsonb 安全吗?

PowerShell - 将 json 添加到文件内容

在 postgres 14 中将记录转换为所需的 json 格式

APIM 生成 JsonArray 到 EventHub

通过 xslt 将内部 json 转换为 xml 时遇到问题

将 JSON 解组为具有唯一元素的 map 切片

Json.NET SerializeObject 转义值以防止 XSS

JSON Schema 与 XML Schema 的比较及其future

JSON 模式验证

.NET 对象最灵活的序列化是什么,但实现起来很简单?

使用 Spring 和 JsonTypeInfo 注释将 JSON 反序列化为多态对象模型

PostgreSQL 中的 JSON 模式验证?

waitUntilAllTask​​sAreFinished 错误 Swift

使用 application/json 优于 text/plain 的优势?