我有一个类,它是一个内部列表的装饰器。我想在我的JAX-RS服务中将此类用作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,从而将其序列化为一个数组。但我需要将它序列化为一个对象。我该怎么做,实现List接口?
答案 0 :(得分:1)
假设Jackson是您的序列化程序,您可以将ObjectMapper
配置为WRAP_ROOT_VALUE
。你会在ContextResolver
中做到这一点。因此,所有类型都不使用相同的配置,您可以使用两个不同的已配置ObjectMapper
,一个用于列表类,另一个用于其余类型。例如
@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
将调用getContext
方法,传入它正在尝试编组的类。根据结果,即将使用的ObjectMapper
。 WRAP_ROOT_VALUE
的作用是将根值包装在对象中,其名称为@JsonRootName
或@XmlRootElement
中的值(给定JAXB注释支持已启用 - 请参阅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
的结果:强>
{ "movies" : [ { "name" : "I Origins" }, { "name" : "Imitation Game" } ] }
所以我注意到你的列表是protected
。也许你以后可能想要扩展MovieList
类。在这种情况下,这个
if (type == MovieList.class) {
return listMapper;
}
将是可行的。您需要检查类型isAssignableFrom
if (MovieList.class.isAssignableFrom(type)) {
return listMapper;
}