强制 JAX-RS 将我的 class 序列化为 JSON 对象

Force JAX-RS to serialize my class to a JSON object

我有 class,它是围绕内部列表的装饰器。我想在我的 JAX-RS 服务中使用这个 class 作为 DTO。其代码如下:

@XmlRootElement(name = "movies")
public class MoviesResponse implements List<Movie> {

    @XmlElement(name = "movie")
    protected List<Movie> movies;

    /* tons of delegate methods */

}

我需要同时支持 application/xml 和 application/json。 格式是固定的,必须像

<movies>
 <movie>
  <foo />
 </movie>
 <movie>
  <foo /> 
 </movie>
</movies>

... 在 XML 和

{
 "movie": [
 {},{}
 ]
}

...在 JSON 中。 XML 工作得很好,但是 JSON 看起来像这样:

[{},{}]

您可能会怀疑,如果我不实现 List 接口,它会生成我需要的格式。所以我猜序列化器很聪明,把它当作列表来序列化成一个数组。但是我需要将它序列化为一个对象。我该怎么做,实现 List 接口?

假设 Jackson 是您的序列化程序,您可以将 ObjectMapper 配置为 WRAP_ROOT_VALUE. You would do that in the ContextResolver。为了不对所有类型使用相同的配置,您可以使用两种不同的配置 ObjectMapper,一种用于列表 class,另一种用于其余。例如

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {

    final ObjectMapper listMapper = new ObjectMapper();
    final ObjectMapper defaultMapper = new ObjectMapper();

    public ObjectMapperContextResolver() {
        listMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        listMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);

        listMapper.registerModule(new JaxbAnnotationModule());
        defaultMapper.registerModule(new JaxbAnnotationModule());
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        if (type == MovieList.class) {
            return listMapper;
        }
        return defaultMapper;
    }  
}

MessageBodyWriter used for marshalling will call the getContext method, passing in the class it's trying to marshal. Based on the the result, that is the ObjectMapper that will be used. What WRAP_ROOT_VALUE does, is wrap the root value in a object, with the name being the value in @JsonRootName or @XmlRootElement (given JAXB annotation support is enabled- see here)

测试:

@Path("/movies")
public class MovieResource {

    @GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Response getMovieList() {
        MovieList list = new MovieList();
        list.add(new Movie("I Origins"));
        list.add(new Movie("Imitation Game"));
        return Response.ok(list).build();
    }
}

C:\>curl -v -H "Accept:application/json" http://localhost:8080/api/movies
Result:
{ "movies" : [ { "name" : "I Origins" }, { "name" : "Imitation Game" } ] }

更新

所以我注意到你的列表是 protected。也许您以后可能想要扩展 MovieList class。在这种情况下,这个

if (type == MovieList.class) {
    return listMapper;
}

机器人是否可行。您需要检查类型 isAssignableFrom

if (MovieList.class.isAssignableFrom(type)) {
    return listMapper;
}