日付と時刻のSpringカスタム(de)シリアル化

かなり一般的な状況を想像してみてください。アプリケーションは、異なるタイムゾーンにあるクライアントと対話します。多くの場合、日付を操作する必要があり、システムが正しく機能するために、日付は送信者のタイムゾーンで送信されます。そうすることで、あなたは必要です:





  1. リクエストを受信したら、日付をサーバー時間に持ってきて処理し、このフォームでデータベースに保存します





  2. 応答として、サーバーのタイムゾーンを示す日付と時刻を返します





これを実現するために、Springはカスタムのシリアル化と逆シリアル化を作成するための便利なメカニズムを提供します。その主な利点は、日付変換(およびその他のデータ型)を別の構成クラスに移動し、ソースコードで毎回変換メソッドを呼び出さない機能です。





デシリアライズ

Springが(de)シリアル化に使用する必要があるのはクラスであることを理解するには、アノテーションを付ける必要があります @JsonComponent







コードをできるだけ簡潔にするために、内部静的クラスを使用します。このクラスは、JsonDeserializer



必要なデータ型から継承し、パラメーター化する必要があります。これJsonDeserializer



 は抽象クラスであるため、その抽象メソッドをオーバーライドする必要がありますdeserialize()







@JsonComponent
public class CustomDateSerializer {  
  
    public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
    
        @Override
        public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
            return null;
        }
    }
}
      
      



メソッドパラメータから、クライアントから渡された文字列を取得し、nullをチェックして、そこからクラスオブジェクトを取得します ZonedDateTime







public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
    String date = jsonParser.getText();
    if (date.isEmpty() || isNull(date) {
        return null;
    }
    ZonedDateTime userDateTime = ZonedDateTime.parse(date);
}
      
      



, userDateTime



withZoneSameInstant()



. LocalDateTime







, , , . , .





public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {

    @Override
    public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        String date = jsonParser.getText();
        if (date.isEmpty()) {
            return null;
        }
        try {
            ZonedDateTime userDateTime = ZonedDateTime.parse(date);
            ZonedDateTime serverTime = userDateTime.withZoneSameInstant(ZoneId.systemDefault());
            return serverTime.toLocalDateTime();
        } catch (DateTimeParseException e) {
            try {
                return LocalDateTime.parse(date);
            } catch (DateTimeParseException ex) {
                throw new IllegalArgumentException("Error while parsing date", ex);
            }
        }
    }
}
      
      



,  UTC+03. , 2021-01-21T22:00:00+07:00



,





public class Subscription {

    private LocalDateTime startDate;
  
    // standart getters and setters
}
      
      



@RestController 
public class TestController {
  
  @PostMapping
  public void process(@RequestBody Subscription subscription) {
    //     startDate  subscription   2021-01-21T18:00
  }
}
      
      



. JsonSerializer



, serialize()







null, .





public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

    @Override
    public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if (isNull(localDateTime)) {
            return;
        }
        OffsetDateTime timeUtc = localDateTime.atOffset(ZoneOffset.systemDefault().getRules().getOffset(LocalDateTime.now()));
        jsonGenerator.writeString(timeUtc.toString());
    }
}
      
      



? ? . , , , UTC+00. , id . ZoneOffset







, UTC+03, : 2021-02-21T18:00+03:00.



UTC+00, 2021-02-21T18:00Z







文字列を使用しているので、コードを少し変更して、出力で常に同じ形式の日付を取得できるようにすることは難しくありません。2つの定数を宣言しましょう-1つはデフォルトIDUTC + 00に等しく、もう1つはクライアントに与えてチェックを追加します-サーバー時間がゼロタイムゾーンにある場合は、に置き換えZ



られ+00:00



ます。その結果、シリアライザーは次のようになります





public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

    private static final String UTC_0_OFFSET_ID = "Z";
    private static final String UTC_0_TIMEZONE = "+00:00";

    @Override
    public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if (!isNull(localDateTime)) {
            String date;
            OffsetDateTime timeUtc = localDateTime.atOffset(ZoneOffset.systemDefault().getRules().getOffset(LocalDateTime.now()));
            if (UTC_0_OFFSET_ID.equals(timeUtc.getOffset().getId())) {
                date = timeUtc.toString().replace(UTC_0_OFFSET_ID, UTC_0_TIMEZONE);
            } else {
                date = timeUtc.toString();
            }
            jsonGenerator.writeString(date);
        }
    }
}
      
      



合計

組み込みのスプリングメカニズムのおかげで、コードで明示的なメソッドを呼び出すことなく、必要な形式で日付と時刻を自動的に変換することができました。





完全なソースコードはここで見ることができます








All Articles