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

102

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

@RestController
public class EventController {

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

}

Итак, Spring MVC неявно использует Jackson для сериализации моего 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.

Йоник
источник
Вы вставили эту аннотацию ?: @SuppressWarnings ({"SpringJavaAutowiringInspection"})
sven.kwiotek
Обратите внимание, что если вы также используете Spring Web, вам нужно будет указать ему вручную использовать этот ObjectMapper, иначе он создаст собственный экземпляр, который не будет настроен. См stackoverflow.com/questions/7854030/...
Константино Cronemberger

Ответы:

117

Если вы используете 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 - объявить собственный, соответствующим образом настроенный Jackson2ObjectMapperBuilderbean-компонент. Например:

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.serializationInclusion(JsonInclude.Include.NON_NULL);
    return builder;
}
Энди Уилкинсон
источник
1
Хорошо, похоже, это работает. Где бы вы поместили такой метод? Может быть, в основном классе Application (с @ComponentScanи @EnableAutoConfigurationт.д.)?
Jonik
2
Да. Этот метод может входить в любой @Configurationкласс вашего приложения. Основной Applicationкласс - хорошее место для этого.
Энди Уилкинсон
2
Важное примечание: класс Jackson2ObjectMapperBuilder является частью компонента spring -web и был добавлен в версии 4.1.1.
gaoagong
2
@ Устаревший setSerializationInclusion
Дмитрий Коприва
6
Устарело: ObjectMapper.setSerializationInclusion устарел в Jackson 2.7 ... вместо этого используйте stackoverflow.com/a/44137972/6785908 spring.jackson.default-property -clusion = non_null
so-random-dude
24

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

@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()));
    }
}
Виджай Веерарагхаван
источник
1
В последней версии вы должны реализоватьWebMvcConfigurer
Жоао Педро Шмитт
да, народ, попробуйте это решение, я попытался предоставить Bean для ObjectMapper, затем Bean для Jackson2ObjectMapperBuilder купить, что не сработало, и я до сих пор не понимаю, почему. Прикрепление кутера сработало!
Somal Somalski,
22

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

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

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

nbank
источник
Это было решение, которое спасло мне день. За исключением того, что я добавил этот метод в сам контроллер REST, а не в класс конфигурации.
Нестор Миляев
21

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

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

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

Предраг Марич
источник
2
Как сделать то же самое без замены дефолтного ObjectMapper? Я имею в виду также оставить значение по умолчанию как обычное.
soufrk
13

Вы можете добавить в свой класс начальной загрузки следующий метод, помеченный как @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;
}
sendon1982
источник
Этот сработал для меня с использованием Boot 2.1.3. Свойства spring.jackson не повлияли.
Richtopia
9

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

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

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

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

Устарело: ObjectMapper.setSerializationInclusion устарел в Jackson 2.7.

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

такой случайный чувак
источник
4

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

По сути, выполните только шаг 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 В моем случае не обязательно, но почему бы не сделать это явным.)

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

  • Это проще; Я могу просто расширить класс от Jackson, и мне не нужно знать о таких специфичных для Spring вещах, как Jackson2ObjectMapperBuilder.
  • Я хочу использовать те же конфигурации Джексона для десериализации JSON в другой части моего приложения, и это очень просто: new CustomObjectMapper()вместо new ObjectMapper().
Йоник
источник
Обратной стороной этого подхода является то, что ваша настраиваемая конфигурация не будет применяться ни к каким ObjectMapperэкземплярам, ​​созданным или настроенным с помощью Spring Boot.
Энди Уилкинсон
Хм, пользовательские конфигурации будет использоваться для неявного сериализации в @RestControllerклассах , которые в настоящее время достаточно для меня. (Вы имеете в виду, что эти экземпляры создаются Spring MVC, а не Spring Boot?) Но если я столкнусь с другими случаями, когда необходимо создать экземпляры ObjectMappers, я буду помнить о подходе Jackson2ObjectMapperBuilder!
Jonik 05
3

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

Также см. Https://stackoverflow.com/a/48519868/255139

@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;
}
Кальпеш Сони
источник
1

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

spring.jackson.serialization -clusion = non_null

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

Я проверил это, экспериментируя с модификацией образца пружинных ботинок "Spring-Boot-Sample-Jersey".

Тим Дуган
источник
0

Я знаю вопрос о загрузке 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();
}
Сэм YC
источник