如何自定义JAXB对象列表到JSON的序列化?

Alp*_*rae 54 serialization json jax-rs jaxb jersey

我正在使用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
}
Run Code Online (Sandbox Code Playgroud)

我有一个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))
}
Run Code Online (Sandbox Code Playgroud)

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

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

我使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

REST资源和上下文解析器都可以工作.这是第一个输出的示例:

// path: /distribution/1
{"id":1,"name":"Example Distribution"}
Run Code Online (Sandbox Code Playgroud)

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

// path: /distributions
{"distribution":[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]}
Run Code Online (Sandbox Code Playgroud)

这不是我想要的.

我不明白为什么那里有一个封闭的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"}]}}
Run Code Online (Sandbox Code Playgroud)

我还必须配置.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"}]
Run Code Online (Sandbox Code Playgroud)

我试图返回a List<XMLDistribution>,一个XMLDistributionList(列表中的包装),a XMLDistribution[],但我找不到一种方法来获得我所需格式的简单JSON数组.

我也试图通过返回的其他符号JSONConfiguration.natural(),JSONConfiguration.mappedJettison()等,并不能得到任何类似我需要什么.

有谁知道是否有可能配置JAXB来做到这一点?

Jon*_*han 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>
Run Code Online (Sandbox Code Playgroud)

这就是它的全部内容.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/


小智 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>
Run Code Online (Sandbox Code Playgroud)