Заставить JAX-RS сериализовать мой класс в объект JSON
У меня есть класс, который является декоратором вокруг внутреннего списка. Я хотел бы использовать этот класс в качестве DTO в моей службе JAX-RS. Его код выглядит следующим образом:
@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?
1 ответ
Предполагая, что Джексон ваш сериализатор, вы можете настроить ObjectMapper
в WRAP_ROOT_VALUE
, Вы бы сделали это в ContextResolver
, Так что одна и та же конфигурация не используется для всех типов, вы можете использовать два разных ObjectMapper
s, один для класса списка и один для остальных. Например
@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 включена - см. здесь)
Тестовое задание:
@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;
}