How to customise the Jackson JSON mapper implicitly used by Spring Boot?

I'm using Spring Boot (1.2.1), in a similar fashion as in their Building a RESTful Web Service tutorial:

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

So above, Spring MVC implicitly uses Jackson for serialising my EventList object into JSON.

But I want to do some simple customisations to the JSON format, such as:

setSerializationInclusion(JsonInclude.Include.NON_NULL)

Question is, what is the simplest way to customise the implicit JSON mapper?

I tried the approach in this blog post, creating a CustomObjectMapper and so on, but the step 3, "Register classes in the Spring context", fails:

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: {}

It looks like those instructions are for older versions of Spring MVC, while I'm looking for a simple way to get this working with latest Spring Boot.

205128 次浏览

文档 说明了实现这一点的几种方法。

如果要完全替换默认的 ObjectMapper,请定义该类型的 @Bean并将其标记为 @Primary

定义 Jackson2ObjectMapperBuilder类型的 @Bean将允许您定制默认的 ObjectMapperXmlMapper(分别在 MappingJackson2HttpMessageConverterMappingJackson2XmlHttpMessageConverter中使用)。

您可以通过 application.properties配置属性包含和许多其他设置:

spring.jackson.default-property-inclusion=non_null

文件中有一个表列出了所有可以使用的属性。

如果您想要更多的控制,您还可以使用 Jackson2ObjectMapperBuilderCustomizer bean 以编程方式自定义 Spring Boot 的配置,如 文件中所述:

上下文的 Jackson2ObjectMapperBuilder可以由一个或多个 Jackson2ObjectMapperBuilderCustomizer bean 定制。这样的定制器 bean 可以订购(Boot 自己的定制器的订单为0) ,这样就可以在 Boot 的定制之前和之后应用额外的定制。

最后,如果您不想要任何 Boot 的配置,并且希望完全控制 ObjectMapper的配置方式,那么声明您自己的 Jackson2ObjectMapperBuilder bean:

@Bean
Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
// Configure the builder to suit your needs
return builder;
}

我偶然发现了另一种解决方案,这是相当不错的。

基本上,只执行 第二步从博客上提到,并将自定义 ObjectMapper 定义为 Spring@Component。(当我从第3步开始使用 AnnotationMethodHandlerAdapter 的所有东西时,事情就开始工作了。)

@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
  • 我想在我的应用程序的另一部分使用相同的 Jackson 配置来反序列化 JSON,这样做非常简单: new CustomObjectMapper()而不是 new ObjectMapper()

对于这个问题,我回答得有点晚,但是将来可能会有人发现这个有用。下面的方法,除了许多其他方法之外,效果最好,我个人认为更适合 Web 应用程序。

@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.3中有,但是您可以添加一个 Config-Class

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

[更新: 必须使用 ObjectMapper,因为在运行配置之前调用了 build()方法。]

我找到了上面描述的解决方案:

spring.jackson.serialization-inclusion=non_null

只能从1.4.0. RELEASE 版本的 Spring 启动开始工作。在所有其他情况下,该配置都被忽略。

我通过对弹簧启动样本“ spring-boot-sample-jersey”进行修改来验证这一点

spring.jackson.serialization-inclusion=non_null以前为我们工作

但是当我们将 Spring 启动版本升级到1.4.2. RELEASE 或更高版本时,它就停止工作了。

现在,另一个属性 spring.jackson.default-property-inclusion=non_null正在起作用。

事实上,serialization-inclusion是不被推荐的。这就是我的智慧抛给我的。

Deprecated: ObjectMapper.setSerializationInclusion was deprecated in 杰克逊2.7

因此,开始使用 spring.jackson.default-property-inclusion=non_null代替

我知道问春季启动的问题,但我相信很多人都在寻找如何在非春季启动这样做,喜欢我搜索几乎整天。

在 Spring4之上,如果您只想配置 ObjectMapper,则不需要配置 MappingJacksonHttpMessageConverter

你只需要做:

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

You can add a following method inside your bootstrap class which is annotated with @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 启动2.0.6中使 ObjectMapper 成为主要的时候,出现了错误 So I modified the one that spring boot created for me

也请参阅 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;
}

将进一步的配置添加到 SpringBoot peconfigureObjectMapper 的正确方法是定义一个 Jackson2ObjectMapperBuilderCustomizer。否则,您将覆盖您不希望丢失的 Spring 配置。

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

关于这个问题,我已经看到了许多问题。这就是我在 SpringBoot2.7.0-SNAPSHOT 中的工作原理。

我创建了一个配置,MapperConfigs,创建了一个 objectMapper bean,如 文件所说,注释了主要内容

@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;
...
}

删除默认转换器并添加自定义转换器:

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

注意: 请不要使用来自 WebMvcConfigrer 的 configureMessageConverters ()而不是 extendMessageConverters () ,因为 configure 方法将删除默认安装的所有现有转换器。

希望它能帮助像我这样浪费了几个小时来调试这个问题的人:)