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

Проблема

У меня есть приложение Spring MVC, которое требует от меня перевести id и имена списка определенного объекта в массив объектов 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, но я не мог найти правильное использование их внутри контекста где-нибудь.

Вопрос

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

Ответ 1

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

Ответ 2

  @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());
    }
}