Заставить 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;
}
Другие вопросы по тегам