如何自定义 Spring Boot 隐式使用的 Jackson JSON 映射器?
How to customise the Jackson JSON mapper implicitly used by Spring Boot?
我正在使用 Spring Boot (1.2.1),其方式与他们的 Building a RESTful Web Service 教程类似:
@RestController
public class EventController {
@RequestMapping("/events/all")
EventList events() {
return proxyService.getAllEvents();
}
}
以上,Spring MVC 隐式使用 Jackson 将我的 EventList
对象序列化为 JSON.
但我想对 JSON 格式做一些简单的自定义,例如:
setSerializationInclusion(JsonInclude.Include.NON_NULL)
问题是,自定义隐式 JSON 映射器的最简单方法是什么?
我尝试了 this blog post 中的方法,创建了一个 CustomObjectMapper 等等,但是第 3 步,“Register 类 in the 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。
The documentation 说明了几种方法。
If you want to replace the default ObjectMapper
completely, define a @Bean
of that type and mark it as @Primary
.
Defining a @Bean
of type Jackson2ObjectMapperBuilder
will allow you to customize both default ObjectMapper
and XmlMapper
(used in MappingJackson2HttpMessageConverter
and MappingJackson2XmlHttpMessageConverter
respectively).
您可以配置 属性 包含和许多其他设置,通过 application.properties
:
spring.jackson.default-property-inclusion=non_null
the documentation 中有一个 table 列出了所有可以使用的属性。
如果您想要更多控制,您还可以使用 Jackson2ObjectMapperBuilderCustomizer
bean 以编程方式自定义 Spring 引导配置,如 documentation:
中所述
The context’s Jackson2ObjectMapperBuilder
can be customized by one or more Jackson2ObjectMapperBuilderCustomizer
beans. Such customizer beans can be ordered (Boot’s own customizer has an order of 0), letting additional customization be applied both before and after Boot’s customization.
最后,如果您不需要 Boot 的任何配置并希望完全控制 ObjectMapper
的配置方式,请声明您自己的 Jackson2ObjectMapperBuilder
bean:
@Bean
Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
// Configure the builder to suit your needs
return builder;
}
我偶然发现了另一种解决方案,非常好。
基本上只做step 2 from the blog posted mentioned,定义一个自定义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 扩展一个 class 而不需要了解高度 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-包含=non_null
仅从 1.4.0.RELEASE 版本的 spring 引导开始工作。在所有其他情况下,配置将被忽略。
我通过修改 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
已被弃用。这就是我的 intellij 抛给我的东西。
Deprecated: ObjectMapper.setSerializationInclusion was deprecated in
Jackson 2.7
所以,开始使用 spring.jackson.default-property-inclusion=non_null
而不是
我知道要求 Spring 引导的问题,但我相信很多人都在寻找如何在非 Spring 引导中执行此操作,就像我搜索几乎一整天一样。
以上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();
}
您可以在 bootstrap class 中添加以下方法,并用 @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 boot 2.0.6 中将 ObjectMapper 设置为主时,出现错误
所以我修改了 spring boot 为我创建的
另见
@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;
}
向 Spring 引导预配置的 ObjectMapper 添加更多配置的正确方法是定义 Jackson2ObjectMapperBuilderCustomizer。否则您将覆盖 Spring 的配置,您不想丢失它。
@Configuration
public class MyJacksonConfigurer implements Jackson2ObjectMapperBuilderCustomizer {
@Override
public void customize(Jackson2ObjectMapperBuilder builder) {
builder.deserializerByType(LocalDate.class, new MyOwnJsonLocalDateTimeDeserializer());
}
}
我已经看到很多关于这个问题的问题。这就是 Spring 引导版本 2.7.0-SNAPSHOT 中对我有用的东西。
我创建了一个配置 MapperConfigs,创建了一个 objectMapper bean,如 documentation 所说
注释为主要
@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;
...
}
我正在使用 Spring Boot (1.2.1),其方式与他们的 Building a RESTful Web Service 教程类似:
@RestController
public class EventController {
@RequestMapping("/events/all")
EventList events() {
return proxyService.getAllEvents();
}
}
以上,Spring MVC 隐式使用 Jackson 将我的 EventList
对象序列化为 JSON.
但我想对 JSON 格式做一些简单的自定义,例如:
setSerializationInclusion(JsonInclude.Include.NON_NULL)
问题是,自定义隐式 JSON 映射器的最简单方法是什么?
我尝试了 this blog post 中的方法,创建了一个 CustomObjectMapper 等等,但是第 3 步,“Register 类 in the 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。
The documentation 说明了几种方法。
If you want to replace the default
ObjectMapper
completely, define a@Bean
of that type and mark it as@Primary
.Defining a
@Bean
of typeJackson2ObjectMapperBuilder
will allow you to customize both defaultObjectMapper
andXmlMapper
(used inMappingJackson2HttpMessageConverter
andMappingJackson2XmlHttpMessageConverter
respectively).
您可以配置 属性 包含和许多其他设置,通过 application.properties
:
spring.jackson.default-property-inclusion=non_null
the documentation 中有一个 table 列出了所有可以使用的属性。
如果您想要更多控制,您还可以使用 Jackson2ObjectMapperBuilderCustomizer
bean 以编程方式自定义 Spring 引导配置,如 documentation:
The context’s
Jackson2ObjectMapperBuilder
can be customized by one or moreJackson2ObjectMapperBuilderCustomizer
beans. Such customizer beans can be ordered (Boot’s own customizer has an order of 0), letting additional customization be applied both before and after Boot’s customization.
最后,如果您不需要 Boot 的任何配置并希望完全控制 ObjectMapper
的配置方式,请声明您自己的 Jackson2ObjectMapperBuilder
bean:
@Bean
Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
// Configure the builder to suit your needs
return builder;
}
我偶然发现了另一种解决方案,非常好。
基本上只做step 2 from the blog posted mentioned,定义一个自定义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 扩展一个 class 而不需要了解高度 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-包含=non_null
仅从 1.4.0.RELEASE 版本的 spring 引导开始工作。在所有其他情况下,配置将被忽略。
我通过修改 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
已被弃用。这就是我的 intellij 抛给我的东西。
Deprecated: ObjectMapper.setSerializationInclusion was deprecated in Jackson 2.7
所以,开始使用 spring.jackson.default-property-inclusion=non_null
而不是
我知道要求 Spring 引导的问题,但我相信很多人都在寻找如何在非 Spring 引导中执行此操作,就像我搜索几乎一整天一样。
以上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();
}
您可以在 bootstrap class 中添加以下方法,并用 @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 boot 2.0.6 中将 ObjectMapper 设置为主时,出现错误 所以我修改了 spring boot 为我创建的
另见
@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;
}
向 Spring 引导预配置的 ObjectMapper 添加更多配置的正确方法是定义 Jackson2ObjectMapperBuilderCustomizer。否则您将覆盖 Spring 的配置,您不想丢失它。
@Configuration
public class MyJacksonConfigurer implements Jackson2ObjectMapperBuilderCustomizer {
@Override
public void customize(Jackson2ObjectMapperBuilder builder) {
builder.deserializerByType(LocalDate.class, new MyOwnJsonLocalDateTimeDeserializer());
}
}
我已经看到很多关于这个问题的问题。这就是 Spring 引导版本 2.7.0-SNAPSHOT 中对我有用的东西。
我创建了一个配置 MapperConfigs,创建了一个 objectMapper bean,如 documentation 所说
注释为主要@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;
...
}