Frühlingsspezifische (De-) Serialisierung von Datum und Uhrzeit

Stellen Sie sich eine ziemlich häufige Situation vor: Ihre Anwendung interagiert mit Clients, die sich in verschiedenen Zeitzonen befinden. Sie müssen häufig mit Datumsangaben arbeiten, und damit das System ordnungsgemäß funktioniert, werden diese mit der Zeitzone des Absenders gesendet. Dazu benötigen Sie:





  1. Wenn eine Anfrage eingeht, bringen Sie das Datum zur Serverzeit und arbeiten Sie damit. Speichern Sie es auch in diesem Formular in der Datenbank





  2. Geben Sie als Antwort das Datum und die Uhrzeit zurück, die die Serverzeitzone angeben





Um dies zu erreichen, bietet Spring einen praktischen Mechanismus zum Schreiben einer benutzerdefinierten Serialisierung und Deserialisierung. Der Hauptvorteil besteht in der Möglichkeit, Datumskonvertierungen (und andere Datentypen) in eine separate Konfigurationsklasse zu verschieben und nicht jedes Mal im Quellcode Konvertierungsmethoden aufzurufen.





Deserialisierung

Damit Spring versteht, dass es unsere Klasse ist, die für die (De-) Serialisierung verwendet werden muss, muss sie mit der Anmerkung gekennzeichnet werden @JsonComponent







Um den Code so präzise wie möglich zu halten, werde ich eine interne statische Klasse verwenden, die von JsonDeserializer



dem von uns benötigten Datentyp geerbt und parametrisiert werden muss. Da es sich JsonDeserializer



 um eine abstrakte Klasse handelt, müssen wir ihre abstrakte Methode überschreibendeserialize()







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



Aus den Methodenparametern erhalten wir die vom Client übergebene Zeichenfolge, überprüfen sie auf null und erhalten ein Klassenobjekt daraus 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







Da wir mit einer Zeichenfolge arbeiten, wird es für uns nicht schwierig sein, den Code ein wenig zu ändern, damit wir das Datum bei der Ausgabe immer im gleichen Format erhalten. Deklarieren wir zwei Konstanten - eine davon entspricht der Standard-ID UTC + 00 und die zweite - die wir dem Client geben möchten, und fügen eine Überprüfung hinzu - wenn die Serverzeit in der Zeitzone Null liegt, dann wir wird es Z



durch ersetzen +00:00



. Infolgedessen sieht unser Serializer so aus





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);
        }
    }
}
      
      



Gesamt

Dank der integrierten Federmechanismen konnten wir Datum und Uhrzeit automatisch in das erforderliche Format konvertieren, ohne dass explizite Methodenaufrufe im Code erforderlich waren





Der vollständige Quellcode kann hier eingesehen werden








All Articles