Как настроить маппер Jackson JSON, неявно используемый Spring Boot?

Я использую Spring Boot (1.2.1), так же, как в руководстве по созданию веб-службы RESTful:

@RestController
public class EventController {

    @RequestMapping("/events/all")
    EventList events() {
        return proxyService.getAllEvents();
    }

}

Итак, выше, Spring MVC неявно использует Джексона для сериализации моего EventList объект в JSON.

Но я хочу сделать несколько простых настроек в формате JSON, таких как:

setSerializationInclusion(JsonInclude.Include.NON_NULL)

Вопрос в том, как проще всего настроить неявное отображение JSON?

Я попробовал подход в этом сообщении в блоге, создав CustomObjectMapper и т. Д., Но шаг 3, "Регистрация классов в контексте Spring", завершился неудачно:

org.springframework.beans.factory.BeanCreationException: 
  Error creating bean with name 'jacksonFix': Injection of autowired dependencies failed; 
  nested exception is org.springframework.beans.factory.BeanCreationException: 
  Could not autowire method: public void com.acme.project.JacksonFix.setAnnotationMethodHandlerAdapter(org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter); 
  nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
  No qualifying bean of type [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter]   
  found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

Похоже, что эти инструкции предназначены для более старых версий Spring MVC, в то время как я ищу простой способ заставить это работать с последней версией Spring Boot.

14 ответов

Решение

Если вы используете Spring Boot 1.3, вы можете настроить включение сериализации через application.properties:

spring.jackson.serialization-inclusion=non_null

После изменений в Jackson 2.7 Spring Boot 1.4 использует свойство с именем spring.jackson.default-property-inclusion вместо:

spring.jackson.default-property-inclusion=non_null

См. Раздел " Настройка Jackson ObjectMapper" в документации по Spring Boot.

Если вы используете более раннюю версию Spring Boot, самый простой способ настроить включение сериализации в Spring Boot - объявить собственную, соответствующим образом настроенную Jackson2ObjectMapperBuilder боб. Например:

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.serializationInclusion(JsonInclude.Include.NON_NULL);
    return builder;
}

Я отвечаю немного поздно на этот вопрос, но кто-то в будущем может найти это полезным. Приведенный ниже подход, помимо множества других подходов, работает лучше всего, и я лично думаю, что он лучше подойдет для веб-приложения.

@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {

 ... other configurations

@Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        builder.serializationInclusion(JsonInclude.Include.NON_NULL);
        builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
        builder.serializationInclusion(Include.NON_EMPTY);
        builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
        converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
    }
}

В документации указано несколько способов сделать это.

Если вы хотите заменить по умолчанию ObjectMapper полностью определить @Bean такого типа и пометить его как @Primary,

Определение @Bean типа Jackson2ObjectMapperBuilder позволит вам настроить оба по умолчанию ObjectMapper а также XmlMapper (используется в MappingJackson2HttpMessageConverter а также MappingJackson2XmlHttpMessageConverter соответственно).

Многое можно настроить в свойствах приложения. К сожалению, эта функция только в версии 1.3, но вы можете добавить в Config-Class

@Autowired(required = true)
public void configureJackson(ObjectMapper jackson2ObjectMapper) {
    jackson2ObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}

[ОБНОВЛЕНИЕ: Вы должны работать над ObjectMapper, потому что build()-метод вызывается до запуска конфига.]

Вы можете добавить следующий метод в ваш класс начальной загрузки, который аннотирован @SpringBootApplication

    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    ObjectMapper objectMapper = builder.createXmlMapper(false).build();
    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    objectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);

    objectMapper.registerModule(new JodaModule());

    return objectMapper;
}

spring.jackson.serialization-inclusion=non_null раньше работал на нас

Но когда мы обновили весеннюю загрузочную версию до 1.4.2.RELEASE или выше, она перестала работать.

Теперь другое свойство spring.jackson.default-property-inclusion=non_null делает магию

по факту, serialization-inclusion устарела. Это то, что мой интеллиг бросает в меня.

Устаревший: ObjectMapper.setSerializationInclusion устарел в Джексоне 2.7

Итак, начните использовать spring.jackson.default-property-inclusion=non_null вместо

Я наткнулся на другое решение, которое довольно приятно.

В основном, только сделайте шаг 2 из упомянутого блога и определите пользовательский ObjectMapper как Spring @Component, (Все начало работать, когда я просто удалил все вещи AnnotationMethodHandlerAdapter из шага 3.)

@Component
@Primary
public class CustomObjectMapper extends ObjectMapper {
    public CustomObjectMapper() {
        setSerializationInclusion(JsonInclude.Include.NON_NULL); 
        configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 
    }
}

Работает, пока компонент находится в пакете, отсканированном Spring. (С помощью @Primary не является обязательным в моем случае, но почему бы не сделать это явно.)

Для меня есть два преимущества по сравнению с другим подходом:

  • Это проще; Я могу просто расширить класс от Джексона, и мне не нужно знать о таких вещах, как Spring. Jackson2ObjectMapperBuilder,
  • Я хочу использовать те же самые конфиги Джексона для десериализации JSON в другой части моего приложения, и это очень просто: new CustomObjectMapper() вместо new ObjectMapper(),

Когда я попытался сделать ObjectMapper основным в весенней загрузке 2.0.6, у меня возникли ошибки, поэтому я изменил ту, которую создал для меня весенняя загрузка.

Также см. /questions/6234934/kak-nastroit-jackson-v-prilozhenii-vesennej-zagruzki-bez-pereopredeleniya-nastroek-po-umolchaniyu-v-chistom-java/6234943#6234943

@Lazy
@Autowired
ObjectMapper mapper;

@PostConstruct
public ObjectMapper configureMapper() {
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);

    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

    mapper.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, true);
    mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);

    SimpleModule module = new SimpleModule();
    module.addDeserializer(LocalDate.class, new LocalDateDeserializer());
    module.addSerializer(LocalDate.class, new LocalDateSerializer());
    mapper.registerModule(module);

    return mapper;
}

Правильный способ добавления дополнительных конфигураций к настраиваемому объекту ObjectMapper Spring Boot - это определение Jackson2ObjectMapperBuilderCustomizer. В противном случае вы перезаписываете конфигурацию Springs, которую не хотите терять.

 @Configuration
public class MyJacksonConfigurer implements Jackson2ObjectMapperBuilderCustomizer {
    @Override
    public void customize(Jackson2ObjectMapperBuilder builder) {
        builder.deserializerByType(LocalDate.class, new MyOwnJsonLocalDateTimeDeserializer());
    }
}

Я нашел решение, описанное выше с:

spring.jackson.serialization-включение =non_null

Работать только начиная с версии весенней загрузки 1.4.0.RELEASE. Во всех остальных случаях конфиг игнорируется.

Я проверил это, экспериментируя с модификацией образца весенней загрузки "spring-boot-sample-jersey"

Я видел множество вопросов по этому вопросу. Это то, что сработало для меня в Spring Boot версии 2.7.0-SNAPSHOT.

Я создал конфигурацию MapperConfigs, создал bean-компонент objectMapper, аннотировал первичный, как указано в документации .

      @Configuration
@Log4j2
public class MapperConfigs {
    @Bean
    @Primary
    ObjectMapper objectMapper() {
        log.info("Object mapper overrides ");
        return JsonMapper.builder()
                .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .build();
    }
}

Затем я @Autowired objectMapper. Смотри ниже:

      @Service
public class GenerateRequestUniqueID {
    @Autowired
    ObjectMapper objectMapper;
    ...
}

Есть два способа настроить Джексона:

  1. Переопределить поведение по умолчанию для автоматической настройки Spring Boot
  2. ПерезаписатьObjectMapperиметь полный контроль

Переопределить ObjectMapper

      @Configuration
public class CustomJacksonConfig {

    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
        return builder.build().setSerializationInclusion(JsonInclude.Include.NON_NULL)
                .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
                .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                .configure(SerializationFeature.INDENT_OUTPUT, true)
                .registerModule(new JavaTimeModule());
    }
}

Перезаписать ObjectMapper

      @Configuration
public class CustomJacksonConfig {

    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        return new ObjectMapper()
                .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
                .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                .configure(SerializationFeature.INDENT_OUTPUT, true);
    }
}

источник: https://codingnconcepts.com/spring-boot/customize-jackson-json-mapper/

Я знаю вопрос, спрашивающий о загрузке Spring, но я верю, что многие люди ищут, как это сделать при загрузке без Spring, например, я искал почти весь день.

Над Spring 4 нет необходимости настраивать MappingJacksonHttpMessageConverter если вы только собираетесь настроить ObjectMapper,

Вам просто нужно сделать:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

    public MyObjectMapper() {
        super();
        enable(SerializationFeature.INDENT_OUTPUT);
    }       
}

И в вашей конфигурации Spring создайте этот bean-компонент:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}

Удалите стандартный конвертер и добавьте свой собственный конвертер:

      @EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//        Remove the default MappingJackson2HttpMessageConverter
        converters.removeIf(converter -> {
            String converterName = converter.getClass().getSimpleName();
            return converterName.equals("MappingJackson2HttpMessageConverter");
        });
//        Add your custom MappingJackson2HttpMessageConverter
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        converter.setObjectMapper(objectMapper);
        converters.add(converter);
        WebMvcConfigurer.super.extendMessageConverters(converters);
    }
}

Примечание. Не используйте configureMessageConverters() вместо extendMessageConverters() из WebMvcConfigurer, потому что метод configure удалит все существующие конвертеры, которые будут установлены по умолчанию.

Надеюсь, это поможет кому-то вроде меня, который потратил несколько часов на отладку проблемы :)

Другие вопросы по тегам