我正在使用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
}
我有一个REST资源来检索一个看起来像这样的分发:
@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())
}
我使用ContextResolver来自定义JAXB序列化,目前配置如下:
@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;
}
}
两种REST资源都可以使用,以及上下文解析器。这是第一个输出的示例:
// path: /distribution/1
{"id":1,"name":"Example Distribution"}
这正是我想要的。这是列表输出的示例:
// path: /distributions
{"distribution":[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]}
这不是我想要的。
我不明白为什么那里有一个封闭的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数组,即使只有一个元素。
理想情况下,我希望将此作为输出:
// 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分发数组以我要求的格式。
我还尝试了JSONConfiguration.natural()
,JSONConfiguration.mappedJettison()
等返回的其他符号,并且无法获得类似我需要的内容。
有谁知道是否可以配置JAXB来执行此操作?
答案 0 :(得分:102)
我找到了一个解决方案:将JAXB JSON序列化程序替换为像Jackson这样性能更好的JSON序列化程序。简单的方法是使用jackson-jaxrs,它已经为你完成了它。该课程是JacksonJsonProvider。您所要做的就是编辑项目的web.xml,以便Jersey(或其他JAX-RS实现)扫描它。以下是您需要添加的内容:
<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>
这就是它的全部内容。 Jackson将用于JSON序列化,它的工作方式与列表和数组的预期方式相同。
更长的方法是编写自己的自定义MessageBodyWriter,以生成“application / json”。这是一个例子:
@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); } }
您需要确保您的web.xml包含该软件包,就像上面的现成解决方案一样。
无论哪种方式:瞧!你会看到正确形成的JSON。
您可以从这里下载杰克逊: http://jackson.codehaus.org/
答案 1 :(得分:13)
Jonhatan的答案很棒,对我来说非常有用。
只是升级:
如果您使用Jackson的2.x版本(例如版本2.1),则该类为com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider,因此web.xml为:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>your.project.packages;com.fasterxml.jackson.jaxrs.json</param-value>
</init-param>