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:
Wenn eine Anfrage eingeht, bringen Sie das Datum zur Serverzeit und arbeiten Sie damit. Speichern Sie es auch in diesem Formular in der Datenbank
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