【问题标题】:How to customise the Jackson JSON mapper implicitly used by Spring Boot?如何自定义 Spring Boot 隐式使用的 Jackson JSON 映射器?
【发布时间】:2015-04-04 03:36:43
【问题描述】:

我正在使用 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 步“在 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"})
  • 请注意,如果您也使用 Spring Web,则需要手动告诉它使用此 ObjectMapper,否则它将创建自己的实例,该实例不会被配置。见stackoverflow.com/questions/7854030/…

标签: java spring spring-mvc jackson spring-boot


【解决方案1】:

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

spring.jackson.default-property-inclusion=non_null

the documentation 中有一个表格,列出了所有可以使用的属性。

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

上下文的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;
}

【讨论】:

  • 好吧,这似乎有效。你会把这样的方法放在哪里?也许在一个主应用程序类中(@ComponentScan@EnableAutoConfiguration 等)?
  • 是的。此方法可以进入您应用程序中的任何@Configuration 类。主要的Application 类是它的好地方。
  • 重要提示:Jackson2ObjectMapperBuilder 类是 spring-web 组件的一部分,在 4.1.1 版本中添加。
  • @Deprecated setSerializationInclusion
  • 已弃用:ObjectMapper.setSerializationInclusion 在 Jackson 2.7 中已弃用...改用 stackoverflow.com/a/44137972/6785908 spring.jackson.default-property-inclusion=non_null
【解决方案2】:

向 Spring Boot peconfigured ObjectMapper 添加更多配置的正确方法是定义 Jackson2ObjectMapperBuilderCustomizer。否则,您将覆盖 Springs 配置,这是您不想丢失的。

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

【讨论】:

    【解决方案3】:

    The documentation 说明了几种方法来做到这一点。

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

    定义Jackson2ObjectMapperBuilder 类型的@Bean 将允许您自定义默认ObjectMapperXmlMapper(分别用于MappingJackson2HttpMessageConverterMappingJackson2XmlHttpMessageConverter)。

    【讨论】:

    • 如何在不替换默认 ObjectMapper 的情况下做同样的事情?我的意思是保持默认和自定义。
    【解决方案4】:

    当我尝试在 Spring Boot 2.0.6 中将 ObjectMapper 设为主要时,出现错误 所以我修改了spring boot为我创建的那个

    另见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;
    }
    

    【讨论】:

      【解决方案5】:

      可以在应用程序属性中配置很多东西。不幸的是,此功能仅在 1.3 版本中,但您可以添加 Config-Class

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

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

      【讨论】:

      • 这个解决方案拯救了我的一天。除了我确实将此方法添加到 REST 控制器本身,而不是添加到配置类。
      【解决方案6】:

      您可以在您的引导类中添加以下方法,该方法带有 @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;
      }
      

      【讨论】:

      • 这个使用 Boot 2.1.3 对我有用。 spring.jackson 属性没有效果。
      【解决方案7】:

      spring.jackson.serialization-inclusion=non_null曾经为我们工作

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

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

      事实上,serialization-inclusion 已被弃用。这就是我的智能向我扔的东西。

      已弃用:ObjectMapper.setSerializationInclusion 在 杰克逊 2.7

      所以,开始改用spring.jackson.default-property-inclusion=non_null

      【讨论】:

        【解决方案8】:

        我知道要求 Spring Boot 的问题,但我相信很多人都在寻找如何在非 Spring Boot 中执行此操作,就像我一样搜索几乎一整天。

        Spring 4以上,如果只打算配置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();
        }
        

        【讨论】:

          【解决方案9】:

          我找到了上述解决方案:

          spring.jackson.serialization-inclusion=non_null

          仅从 Spring Boot 的 1.4.0.RELEASE 版本开始工作。在所有其他情况下,配置都会被忽略。

          我通过修改弹簧靴示例“spring-boot-sample-jersey”来验证这一点

          【讨论】:

            【解决方案10】:

            我回答这个问题有点晚了,但是将来有人可能会觉得这很有用。除了许多其他方法之外,以下方法效果最好,我个人认为更适合 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()));
                }
            }
            

            【讨论】:

            • 在最近的版本中你必须实现WebMvcConfigurer
            • 是的,人们,试试这个解决方案,我尝试为 ObjectMapper 提供 Bean,然后为 Jackson2ObjectMapperBuilder 购买 Bean,但没有成功,我仍然不知道为什么。附加转换工作!
            【解决方案11】:

            我偶然发现了另一个解决方案,这非常好。

            基本上,只做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 扩展一个类,而无需了解诸如 Jackson2ObjectMapperBuilder 之类的高度特定于 Spring 的内容。
            • 我想在我的应用程序的另一部分使用相同的 Jackson 配置来反序列化 JSON,这样非常简单:new CustomObjectMapper() 而不是 new ObjectMapper()

            【讨论】:

            • 这种方法的缺点是您的自定义配置不会应用于任何由 Spring Boot 创建或配置的 ObjectMapper 实例。
            • 嗯,自定义配置 is 用于 @RestController 类中的隐式序列化,目前对我来说已经足够了。 (所以你的意思是这些实例是由 Spring MVC 创建的,而不是 Spring Boot?)但是如果我遇到需要实例化 ObjectMappers 的其他情况,我会牢记 Jackson2ObjectMapperBuilder 方法!
            猜你喜欢
            • 2018-10-02
            • 1970-01-01
            • 2015-12-23
            • 2020-06-23
            • 2016-12-14
            • 2020-07-04
            • 2012-07-08
            • 2019-04-19
            • 2012-12-31
            相关资源
            最近更新 更多