1. 概要

JSON形式を使用する場合、SpringBootはObjectMapper インスタンスを使用して、応答をシリアル化し、要求を逆シリアル化します。

このチュートリアルでは、シリアル化と逆シリアル化のオプションを構成する最も一般的な方法を見ていきます。

ジャクソンの詳細については、ジャクソンチュートリアルを確認してください。

2. デフォルト設定

デフォルトでは、SpringBoot構成は以下を無効にします。

  • MapperFeature.DEFAULT_VIEW_INCLUSION
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS

簡単な例から始めましょう:

  • クライアントはGETリクエストを/coffee?name=Lavazzaに送信します。
  • コントローラは、新しいCoffeeオブジェクトを返します。
  • SpringはObjectMapperを使用して、POJOをJSONにシリアル化します。

StringおよびLocalDateTimeオブジェクトを使用して、カスタマイズオプションを例示します。

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. アプリケーションのプロパティとカスタムジャクソンモジュール

マッパーを構成する最も簡単な方法は、アプリケーションのプロパティを使用することです。

構成の一般的な構造は次のとおりです。

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を自動的に登録します。 最終結果は次のとおりです。

{
  "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

もう1つのクリーンなアプローチは、 Jackson2ObjectMapperBuilder beanを定義することです。

Spring Bootは、 ObjectMapper をビルドするときに、デフォルトでこのビルダーを実際に使用し、定義されたビルダーを自動的に取得します。

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

デフォルトで2つのオプションを構成します。

  • MapperFeature.DEFAULT_VIEW_INCLUSIONを無効にします
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIESを無効にします

Jackson2ObjectMapperBuilderのドキュメントによると、クラスパスにモジュールが存在する場合は、いくつかのモジュールも登録されます。

  • jackson-datatype-jdk8:オプションなどの他のJava8タイプのサポート
  • jackson-datatype-jsr310:Java8の日付と時刻のAPIタイプのサポート
  • jackson-datatype-joda:Joda-Time型のサポート
  • jackson-module-kotlin:Kotlinクラスとデータクラスのサポート

このアプローチの利点は、 Jackson2ObjectMapperBuilder が、ObjectMapperを構築するためのシンプルで直感的な方法を提供することです。

4.3. MappingJackson2HttpMessageConverter

タイプMappingJackson2HttpMessageConverterでBeanを定義するだけで、SpringBootはそれを自動的に使用します。

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

詳細については、 Spring Http MessageConvertersの記事を確認してください。

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 + "\"}");
}

6. 結論

この記事では、SpringBootを使用するときにJSONシリアル化オプションを構成するためのいくつかの方法について説明しました。

デフォルトのオプションを構成する方法と、デフォルトの構成をオーバーライドする方法の2つの異なるアプローチを見ました。

いつものように、記事の完全なソースコードは、GitHubから入手できます。