ジャクソン日付

ジャクソン・デイト

1. 概要

このチュートリアルでは、Jacksonを使用して日付をシリアル化します。 まず、単純なjava.util。Dateをシリアル化し、次にJoda-TimeとJava 8DateTimeをシリアル化します。

2. Dateをタイムスタンプにシリアル化します

まず、単純なjava.util.Date with Jacksonをシリアル化する方法を見てみましょう。

次の例では、Dateフィールド「eventDate」を持つ「Event」のインスタンスをシリアル化します。

@Test
public void whenSerializingDateWithJackson_thenSerializedToTimestamp()
  throws JsonProcessingException, ParseException {

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));

    Date date = df.parse("01-01-1970 01:00");
    Event event = new Event("party", date);

    ObjectMapper mapper = new ObjectMapper();
    mapper.writeValueAsString(event);
}

ここで重要なのは、ジャクソンがデフォルトで日付をタイムスタンプ形式(1970年1月1日、UTCからのミリ秒数)にシリアル化することです。

event」シリアル化の実際の出力は次のとおりです。

{
   "name":"party",
   "eventDate":3600000
}

3. DateをISO-8601にシリアル化します

この簡潔なタイムスタンプ形式へのシリアル化は最適ではありません。 次に、DateISO-8601形式にシリアル化します。

@Test
public void whenSerializingDateToISO8601_thenSerializedToText()
  throws JsonProcessingException, ParseException {

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));

    String toParse = "01-01-1970 02:30";
    Date date = df.parse(toParse);
    Event event = new Event("party", date);

    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    // StdDateFormat is ISO8601 since jackson 2.9
    mapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true));
    String result = mapper.writeValueAsString(event);
    assertThat(result, containsString("1970-01-01T02:30:00.000+00:00"));
}

日付の表示がより読みやすくなったことに注意してください。

4. ObjectMapperを構成しますDateFormat

以前のソリューションには、java.util.Dateインスタンスを表す正確な形式を選択する完全な柔軟性がまだありません。

ここで、set our formats for representing datesを可能にする構成を見てみましょう。

@Test
public void whenSettingObjectMapperDateFormat_thenCorrect()
  throws JsonProcessingException, ParseException {

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");

    String toParse = "20-12-2014 02:30";
    Date date = df.parse(toParse);
    Event event = new Event("party", date);

    ObjectMapper mapper = new ObjectMapper();
    mapper.setDateFormat(df);

    String result = mapper.writeValueAsString(event);
    assertThat(result, containsString(toParse));
}

日付形式に関してより柔軟になりましたが、ObjectMapper全体のレベルでグローバル構成を使用していることに注意してください。

5. @JsonFormatを使用してDateをフォーマットします

次に、アプリケーション全体について、グローバルではなくcontrol the date format on individual classesへの@JsonFormatアノテーションを見てみましょう。

public class Event {
    public String name;

    @JsonFormat
      (shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
    public Date eventDate;
}

それでは、テストしてみましょう。

@Test
public void whenUsingJsonFormatAnnotationToFormatDate_thenCorrect()
  throws JsonProcessingException, ParseException {

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));

    String toParse = "20-12-2014 02:30:00";
    Date date = df.parse(toParse);
    Event event = new Event("party", date);

    ObjectMapper mapper = new ObjectMapper();
    String result = mapper.writeValueAsString(event);
    assertThat(result, containsString(toParse));
}

6. カスタムDateシリアライザー

次へ–出力を完全に制御するために、Datesのカスタムシリアライザーを活用します。

public class CustomDateSerializer extends StdSerializer {

    private SimpleDateFormat formatter
      = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateSerializer() {
        this(null);
    }

    public CustomDateSerializer(Class t) {
        super(t);
    }

    @Override
    public void serialize (Date value, JsonGenerator gen, SerializerProvider arg2)
      throws IOException, JsonProcessingException {
        gen.writeString(formatter.format(value));
    }
}

次へ–「eventDate」フィールドのシリアライザーとして使用しましょう。

public class Event {
    public String name;

    @JsonSerialize(using = CustomDateSerializer.class)
    public Date eventDate;
}

最後に–それをテストしましょう:

@Test
public void whenUsingCustomDateSerializer_thenCorrect()
  throws JsonProcessingException, ParseException {

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    String toParse = "20-12-2014 02:30:00";
    Date date = df.parse(toParse);
    Event event = new Event("party", date);

    ObjectMapper mapper = new ObjectMapper();
    String result = mapper.writeValueAsString(event);
    assertThat(result, containsString(toParse));
}

参考文献:

Jacksonで列挙型をシリアル化および逆シリアル化する方法

Jackson 2を使用してEnumをJSONオブジェクトとしてシリアライズおよびデシリアライズする方法。

ジャクソン–カスタムシリアライザー

カスタムシリアライザーを使用して、Jackson 2でJSON出力を制御します。

Jacksonでのカスタムデシリアライゼーションの開始

Jacksonを使用して、デシリアライズプロセスを完全に制御して、カスタムJSONをJavaエンティティグラフにマッピングします。

7. Joda-TimeをJacksonとシリアル化する

日付は必ずしもjava.util.Dateのインスタンスではありません。実際、それらは他のクラスによってますます表されています。一般的なものは、もちろん、Joda-TimeライブラリからのDateTime実装です。

serialize DateTime with Jacksonを実行する方法を見てみましょう。

すぐに使用できるJoda-Timeサポートにjackson-datatype-jodaモジュールを使用します。


  com.fasterxml.jackson.datatype
  jackson-datatype-joda
  2.9.7

これで、JodaModuleを登録するだけで、次のことができます。

@Test
public void whenSerializingJodaTime_thenCorrect()
  throws JsonProcessingException {
    DateTime date = new DateTime(2014, 12, 20, 2, 30,
      DateTimeZone.forID("Europe/London"));

    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JodaModule());
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    String result = mapper.writeValueAsString(date);
    assertThat(result, containsString("2014-12-20T02:30:00.000Z"));
}

8. カスタムシリアライザーを使用してJodaDateTimeをシリアル化します

追加のJoda-TimeJackson依存関係が必要ない場合は、a custom serializer(前の例と同様)を使用して、DateTimeインスタンスをクリーンにシリアル化することもできます。

public class CustomDateTimeSerializer extends StdSerializer {

    private static DateTimeFormatter formatter =
      DateTimeFormat.forPattern("yyyy-MM-dd HH:mm");

    public CustomDateTimeSerializer() {
        this(null);
    }

     public CustomDateTimeSerializer(Class t) {
         super(t);
     }

    @Override
    public void serialize
      (DateTime value, JsonGenerator gen, SerializerProvider arg2)
      throws IOException, JsonProcessingException {
        gen.writeString(formatter.print(value));
    }
}

次へ–プロパティ「eventDate」シリアライザーとして使用しましょう。

public class Event {
    public String name;

    @JsonSerialize(using = CustomDateTimeSerializer.class)
    public DateTime eventDate;
}

最後に、すべてをまとめてテストしましょう。

@Test
public void whenSerializingJodaTimeWithJackson_thenCorrect()
  throws JsonProcessingException {

    DateTime date = new DateTime(2014, 12, 20, 2, 30);
    Event event = new Event("party", date);

    ObjectMapper mapper = new ObjectMapper();
    String result = mapper.writeValueAsString(event);
    assertThat(result, containsString("2014-12-20 02:30"));
}

9. JacksonでJava8Dateをシリアル化する

次に– Java 8DateTimeをシリアル化する方法を見てみましょう–この例では、LocalDateTime – using Jacksonです。 jackson-datatype-jsr310モジュールを利用できます。


    com.fasterxml.jackson.datatype
    jackson-datatype-jsr310
    2.9.7

これで、JavaTimeModuleを登録するだけで(JSR310Moduleは非推奨になります)、残りはJacksonが処理します。

@Test
public void whenSerializingJava8Date_thenCorrect()
  throws JsonProcessingException {
    LocalDateTime date = LocalDateTime.of(2014, 12, 20, 2, 30);

    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    String result = mapper.writeValueAsString(date);
    assertThat(result, containsString("2014-12-20T02:30"));
}

10. 余分な依存関係なしにJava8Dateをシリアル化します

追加の依存関係が必要ない場合は、いつでもa custom serializer to write out the Java 8 DateTime to JSONを使用できます。

public class CustomLocalDateTimeSerializer
  extends StdSerializer {

    private static DateTimeFormatter formatter =
      DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

    public CustomLocalDateTimeSerializer() {
        this(null);
    }

    public CustomLocalDateTimeSerializer(Class t) {
        super(t);
    }

    @Override
    public void serialize(
      LocalDateTime value,
      JsonGenerator gen,
      SerializerProvider arg2)
      throws IOException, JsonProcessingException {

        gen.writeString(formatter.format(value));
    }
}

次へ–「eventDate」フィールドにシリアライザーを使用しましょう。

public class Event {
    public String name;

    @JsonSerialize(using = CustomLocalDateTimeSerializer.class)
    public LocalDateTime eventDate;
}

それでは、テストしてみましょう。

@Test
public void whenSerializingJava8DateWithCustomSerializer_thenCorrect()
  throws JsonProcessingException {

    LocalDateTime date = LocalDateTime.of(2014, 12, 20, 2, 30);
    Event event = new Event("party", date);

    ObjectMapper mapper = new ObjectMapper();
    String result = mapper.writeValueAsString(event);
    assertThat(result, containsString("2014-12-20 02:30"));
}

11. Dateを逆シリアル化します

次へ–DateJacksonで逆シリアル化する方法を見てみましょう。 次の例では、日付を含む「Event」インスタンスを逆シリアル化します。

@Test
public void whenDeserializingDateWithJackson_thenCorrect()
  throws JsonProcessingException, IOException {

    String json = "{"name":"party","eventDate":"20-12-2014 02:30:00"}";

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    ObjectMapper mapper = new ObjectMapper();
    mapper.setDateFormat(df);

    Event event = mapper.readerFor(Event.class).readValue(json);
    assertEquals("20-12-2014 02:30:00", df.format(event.eventDate));
}

12. タイムゾーンを保持したままJodaZonedDateTimeを逆シリアル化します

デフォルトの構成では、JacksonはJodaZonedDateTimeのタイムゾーンをローカルコンテキストのタイムゾーンに調整します。 デフォルトでは、ローカルコンテキストのタイムゾーンは設定されておらず、手動で構成する必要があるため、ジャクソンはタイムゾーンをGMTに調整します。

@Test
public void whenDeserialisingZonedDateTimeWithDefaults_thenNotCorrect()
  throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.findAndRegisterModules();
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Europe/Berlin"));
    String converted = objectMapper.writeValueAsString(now);

    ZonedDateTime restored = objectMapper.readValue(converted, ZonedDateTime.class);
    System.out.println("serialized: " + now);
    System.out.println("restored: " + restored);
    assertThat(now, is(restored));
}

このテストケースは出力で失敗します。

serialized: 2017-08-14T13:52:22.071+02:00[Europe/Berlin]
restored: 2017-08-14T11:52:22.071Z[UTC]

幸い、この奇妙なデフォルトの動作には、すばやく簡単な修正があります:we just have to tell Jackson, not to adjust the time zone.

これは、上記のテストケースに以下のコード行を追加することで実行できます。

objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

タイムゾーンを保持するには、日付をタイムスタンプにシリアル化するデフォルトの動作も無効にする必要があることに注意してください。

13. カスタムDateデシリアライザー

a custom Date deserializerの使用方法も見てみましょう。プロパティ「eventDate」のカスタムデシリアライザーを作成します。

public class CustomDateDeserializer extends StdDeserializer {

    private SimpleDateFormat formatter =
      new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateDeserializer() {
        this(null);
    }

    public CustomDateDeserializer(Class vc) {
        super(vc);
    }

    @Override
    public Date deserialize(JsonParser jsonparser, DeserializationContext context)
      throws IOException, JsonProcessingException {
        String date = jsonparser.getText();
        try {
            return formatter.parse(date);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

次へ–「eventDate」デシリアライザーとして使用しましょう。

public class Event {
    public String name;

    @JsonDeserialize(using = CustomDateDeserializer.class)
    public Date eventDate;
}

そして最後に–それをテストしましょう:

@Test
public void whenDeserializingDateUsingCustomDeserializer_thenCorrect()
  throws JsonProcessingException, IOException {

    String json = "{"name":"party","eventDate":"20-12-2014 02:30:00"}";

    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    ObjectMapper mapper = new ObjectMapper();

    Event event = mapper.readerFor(Event.class).readValue(json);
    assertEquals("20-12-2014 02:30:00", df.format(event.eventDate));
}

14. 結論

この広範なDateの記事では、私たちが制御できる適切な形式を使用して、Jackson can help marshalli and unmarshall a date to JSONが最も関連性の高い方法を検討しました。

これらすべての例とコードスニペットcan be found in my GitHub projectの実装–これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。