Использование @JsonSerialize и JsonSerializer

Проблема

У меня есть приложение Spring MVC, которое требует от меня перевода идентификаторов и имен списка определенного объекта в массив объектов JSON с определенным форматированием и вывода этого по определенному запросу. То есть мне нужен массив JSON-объектов вроде этого:

{
     label: Subject.getId()
     value: Subject.getName()
}

Для удобства использования с плагином jQuery Autocomplete.

Итак, в моем контроллере я написал следующее:

@RequestMapping(value = "/autocomplete.json", method = RequestMethod.GET)
@JsonSerialize(contentUsing=SubjectAutocompleteSerializer.class)
public @ResponseBody List<Subject> autocompleteJson() {
    return Subject.findAllSubjects();
}

// Internal class
public class SubjectAutocompleteSerializer extends JsonSerializer<Subject> {

    @Override
    public void serialize(Subject value, JsonGenerator jgen, SerializerProvider provider)
            throws IOException, JsonProcessingException {
        jgen.writeStartObject();
        jgen.writeStringField("label", value.getId().toString());
        jgen.writeStringField("value", value.getName());
        jgen.writeEndObject();
    }
    
}

Однако JSON, который я получаю, является сериализацией по умолчанию, которую вывел Джексон. Мой пользовательский сериализатор, кажется, полностью игнорируется. Очевидно, проблема заключается в неправильном использовании @JsonSerialize или JsonSerializer, но я нигде не смог найти их правильное использование в контексте.

Вопрос

Как правильно использовать Джексона для достижения желаемой сериализации? Обратите внимание, что важно, чтобы сущности были сериализованы только таким образом в этом контексте и открыты для другой сериализации в других местах.


person DCKing    schedule 08.08.2011    source источник
comment
Я понимаю, что эта тема довольно старая, но я сталкиваюсь с той же проблемой. Поскольку принятого ответа нет, мне интересно, нашли ли вы хороший ответ. Как вы указали, нежелательно аннотировать объект напрямую, как это предлагается ниже. ИМХО, подход, который вы пробовали, является самым простым и элегантным/семантическим решением, но, конечно, он не поддерживается. Кажется, вам нужно прыгать через обручи с jackson/spring, чтобы сделать это - единственное решение, которое я нашел, требует создания и настройки пользовательских ObjectMappers и включает в себя довольно много стандартного кода/конфигурации. Есть ли лучший подход?   -  person shelley    schedule 20.03.2012
comment
К сожалению, я не исправил это. Хотя запрос JSON на стороне клиента был нашим первым предпочтением при его реализации, этот конкретный список элементов оказался достаточно маленьким, чтобы сразу разместить его на странице. Это то, что мы сделали, поэтому у нас есть чистое решение на стороне клиента и не нужно загружать JSON. Однако, если вы когда-нибудь найдете лучший способ сделать это, я был бы очень признателен, если бы узнал об этом!   -  person DCKing    schedule 27.03.2012


Ответы (2)


@JsonSerialize должен быть установлен в сериализуемом классе, а не в контроллере.

person henrik_lundgren    schedule 09.08.2011
comment
Я так понял. Есть ли у вас какие-либо указания о том, как реализовать собственный сериализатор JSON в моем контроллере? - person DCKing; 09.08.2011
comment
Я бы пропустил сериализатор и просто аннотировал свойства в классе Subject с помощью @JsonProperty (метка) для идентификатора и @JsonProperty (значение) для имени. - person henrik_lundgren; 10.08.2011
comment
Я комментирую сериализуемый класс, но метод сериализации так и не был вызван. Мне нужно создать ObjectMapper и зарегистрировать его в Spring MVC, как в codeblog.it/en/snippet/java/2013/05/23/ - person Lee Chee Kiam; 16.08.2013

@JsonSerialize должен быть установлен для сериализуемого класса, а не для контроллера.

Я хотел бы добавить свои два цента (пример использования) к приведенному выше ответу... Вы не всегда можете указать сериализатор json для определенного типа, особенно если это общий тип (стирание не позволяет выбрать сериализатор для конкретного универсального во время выполнения), однако вы всегда можете создать новый тип (вы можете расширить обобщенный тип или создать оболочку, если сериализованный тип является окончательным и не может быть расширен) и пользовательский JsonSerializer для этого типа. Например, вы можете сделать что-то подобное для сериализации различных типов org.springframework.data.domain.Page:

@JsonComponent
public class PageOfMyDtosSerializer
        extends JsonSerializer<Page<MyDto>> {
    @Override
    public void serialize(Page<MyDto> page,
                          JsonGenerator jsonGenerator,
                          SerializerProvider serializerProvider)
            throws IOException {
    //...serialization logic for Page<MyDto> type
    }
}

@JsonSerialize(using = PageOfMyDtosSerializer.class)
public class PageOfMyDtos extends PageImpl<MyDto> {
    public PageOfMyDtos(List<MyDto> content, Pageable pageable, long total) {
        super(content, pageable, total);
    }
}

И тогда вы сможете вернуть свой тип из методов ваших сервисов — нужный сериализатор будет использован однозначно:

@Service
@Transactional
public class MyServiceImpl implements MyService {

    ...

    @Override
    public Page<UserProfileDto> searchForUsers(
        Pageable pageable,
        SearchCriteriaDto criteriaDto) {

        //...some business logic

        /*here you pass the necessary search Specification or something else...*/
        final Page<Entity> entities = myEntityRepository.findAll(...);

        /*here you goes the conversion logic of your choice...*/
        final List<MyDto> content = modelMapper.map(entieis.getContent(), new TypeToken<List<MyDto>>(){}.getType());

        /*and finally return your the your new type so it will be serialized with the jsonSerializer we have specified*/
        return new PageOfMyDtos(content, pageable, entities.getTotalElements());
    }
}
person Pavel B    schedule 14.08.2019