Spring boot:自定義Jackson ObjectMapper

    1.概述

    當使用JSON格式時,Spring Boot將使用ObjectMapper實例來序列化響應並反序列化請求。在本教程中,我們將介紹配置序列化和反序列化選項的最常用方法。

    要了解有關Jackson的更多信息,請務必查看我們的Jackson教程。

    2.默認配置

    默認情況下,Spring Boot配置將:

    • 停用MapperFeature.DEFAULT_VIEW_INCLUSION
    • 禁用DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
    • 禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS

    讓我們從一個簡單的例子開始:

    • 客戶將向我們的/coffee?name=Lavazza發送一個GET請求/coffee?name=Lavazza
    • 控制器將返回一個新的Coffee對象
    • Spring將使用ObjectMapper將POJO序列化為JSON

    我們將使用StringLocalDateTime對象舉例說明自定義選項:

    public class Coffee {
    
    
    
     private String name;
    
     private String brand;
    
     private LocalDateTime date;
    
    
    
     //getters and setters
    
     }

    我們還將定義一個簡單的REST控制器來演示序列化

    @GetMapping("/coffee")
    
     public Coffee getCoffee(
    
     @RequestParam(required = false) String brand,
    
     @RequestParam(required = false) String name) {
    
     return new Coffee()
    
     .setBrand(brand)
    
     .setDate(FIXED_DATE)
    
     .setName(name);
    
     }

    默認情況下,調用GET http://lolcahost:8080/coffee?brand=Lavazza時的響應將是:

    {
    
     "name": null,
    
     "brand": "Lavazza",
    
     "date": "2020-11-16T10:21:35.974"
    
     }

    我們想排除null值,並採用自定義日期格式(dd-MM-yyyy HH:mm)。最終答復將是:

    {
    
     "brand": "Lavazza",
    
     "date": "04-11-2020 10:34"
    
     }

    使用Spring Boot時,我們可以選擇自定義默認的ObjectMapper或覆蓋它。我們將在下一部分中介紹這兩個選項。

    3.自定義默認的ObjectMapper

    在本節中,我們將看到如何自定義Spring Boot使用的默認ObjectMapper

    3.1。應用程序屬性和自定義Jackson模塊

    配置映射器的最簡單方法是通過應用程序屬性。配置的一般結構為:

    spring.jackson.<category_name>.<feature_name>=true,false

    例如,如果要禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS ,我們將添加:

    spring.jackson.serialization.write-dates-as-timestamps=false

    除了提到的功能類別,我們還可以配置屬性包含:

    spring.jackson.default-property-inclusion=always, non_null, non_absent, non_default, non_empty
    

    配置環境變量是最簡單的方法。這種方法的缺點是我們無法自定義高級選項,例如為LocalDateTime自定義日期格式。至此,我們將獲得結果:

    {
    
     "brand": "Lavazza",
    
     "date": "2020-11-16T10:35:34.593"
    
     }

    為了實現我們的目標,我們將使用自定義日期格式註冊一個新的JavaTimeModule

    @Configuration
    
     @PropertySource("classpath:coffee.properties")
    
     public class CoffeeRegisterModuleConfig {
    
    
    
     @Bean
    
     public Module javaTimeModule() {
    
     JavaTimeModule module = new JavaTimeModule();
    
     module.addSerializer(LOCAL_DATETIME_SERIALIZER);
    
     return module;
    
     }
    
     }
    

    此外,配置屬性文件coffee.properties將包含:

    spring.jackson.default-property-inclusion=non_null

    Spring Boot將自動註冊com.fasterxml.jackson.databind.Module.類型的任何bean com.fasterxml.jackson.databind.Module.最終結果將是:

    {
    
     "brand": "Lavazza",
    
     "date": "16-11-2020 10:43"
    
     }

    3.2。 Jackson2ObjectMapperBuilderCustomizer

    該功能接口的目的是允許我們創建配置bean。它們將應用於通過Jackson2ObjectMapperBuilder創建的默認ObjectMapper

    @Bean
    
     public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
    
     return builder -> builder.serializationInclusion(JsonInclude.Include.NON_NULL)
    
     .serializers(LOCAL_DATETIME_SERIALIZER);
    
     }

    配置Bean以特定順序應用,我們可以使用@Order批註進行控制。如果我們要從不同的配置或模塊配置ObjectMapper ,則這種優雅的方法非常適合。

    4.覆蓋默認配置

    如果我們想完全控製配置,則有幾個選項將禁用自動配置,並僅允許應用我們的自定義配置。讓我們仔細看看這些選項。

    4.1。 ObjectMapper

    覆蓋默認配置的最簡單方法是定義一個ObjectMapper bean並將其標記為@Primary

    @Bean
    
     @Primary
    
     public ObjectMapper objectMapper() {
    
     JavaTimeModule module = new JavaTimeModule();
    
     module.addSerializer(LOCAL_DATETIME_SERIALIZER);
    
     return new ObjectMapper()
    
     .setSerializationInclusion(JsonInclude.Include.NON_NULL)
    
     .registerModule(module);
    
     }

    當我們想完全控制序列化過程並且我們不想允許外部配置時,應該使用這種方法

    4.2。 Jackson2ObjectMapperBuilder

    另一個乾淨的方法是定義一個Jackson2ObjectMapperBuilder bean .實際上,默認情況下,Spring Boot在構建ObjectMapper時會使用此構建器,並且會自動選擇定義的構建器:

    @Bean
    
     public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
    
     return new Jackson2ObjectMapperBuilder().serializers(LOCAL_DATETIME_SERIALIZER)
    
     .serializationInclusion(JsonInclude.Include.NON_NULL);
    
     }

    默認情況下,它將配置兩個選項:

    • 停用MapperFeature.DEFAULT_VIEW_INCLUSION
    • 禁用DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES

    根據Jackson2ObjectMapperBuilder文檔,如果類路徑中存在某些模塊,它還將註冊一些模塊:

    • jackson-datatype-jdk8:支持其他Java 8類型,例如Optional
    • jackson-datatype-jsr310:支持Java 8日期和時間API類型
    • jackson-datatype-joda:支持Joda-Time類型
    • jackson-module-kotlin:支持Kotlin類和數據類

    這種方法的優點是Jackson2ObjectMapperBuilder提供了一種簡單直觀的方法來構建ObjectMapper

    4.3。 MappingJackson2HttpMessageConverter

    我們可以定義一個類型為MappingJackson2HttpMessageConverter的bean,Spring Boot會自動使用它:

    @Bean
    
     public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    
     Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().serializers(LOCAL_DATETIME_SERIALIZER)
    
     .serializationInclusion(JsonInclude.Include.NON_NULL);
    
     return new MappingJackson2HttpMessageConverter(builder.build());
    
     }

    請務必查看我們的Spring Http Message Converters文章以了解更多信息。

    5.測試配置

    為了測試我們的配置,我們將使用TestRestTemplate並將對象序列化為String 。這樣,我們可以驗證我們的Coffee像是否已序列化,沒有null值並且具有自定義日期格式:

    @Test
    
     public void whenGetCoffee_thenSerializedWithDateAndNonNull() {
    
     String formattedDate = DateTimeFormatter.ofPattern(CoffeeConstants.dateTimeFormat).format(FIXED_DATE);
    
     String brand = "Lavazza";
    
     String url = "/coffee?brand=" + brand;
    
    
    
     String response = restTemplate.getForObject(url, String.class);
    
    
    
     assertThat(response).isEqualTo("{\"brand\":\"" + brand + "\",\"date\":\"" + formattedDate + "\"}");
    
     }

    六,結論

    在本教程中,我們研究了使用Spring Boot時配置JSON序列化選項的幾種方法。

    我們看到了兩種不同的方法:配置默認選項或覆蓋默認配置。