Bayangkan situasi yang cukup umum: aplikasi Anda berinteraksi dengan klien yang berada di zona waktu berbeda. Anda sering kali harus bekerja dengan tanggal, dan agar sistem berfungsi dengan benar, tanggal tersebut dikirim dengan zona waktu pengirim. Dalam melakukannya, Anda membutuhkan:
Saat permintaan diterima, bawa tanggal ke waktu server dan kerjakan dengannya, dan simpan juga ke database dalam formulir ini
Sebagai tanggapan, kembalikan tanggal dan waktu yang menunjukkan zona waktu server
Untuk mencapai hal ini, Spring menyediakan mekanisme yang mudah untuk menulis serialisasi dan deserialisasi kustom. Keuntungan utamanya adalah kemampuan untuk memindahkan konversi tanggal (dan tipe data lainnya) ke dalam kelas konfigurasi terpisah dan tidak memanggil metode konversi setiap kali dalam kode sumber.
Deserialisasi
Agar Spring memahami bahwa kelas kami yang perlu digunakan untuk (de) serialisasi, itu harus ditandai dengan anotasi @JsonComponent
Nah, untuk menjaga kode sesingkat mungkin, saya akan menggunakan kelas statis internal, yang harus diwarisi dari JsonDeserializer
dan diparameterisasi dengan tipe data yang kita butuhkan. Karena ini JsonDeserializer
adalah kelas abstrak, kita perlu mengganti metode abstraknyadeserialize()
@JsonComponent
public class CustomDateSerializer {
public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
return null;
}
}
}
Dari parameter metode, kita mendapatkan string yang diteruskan oleh klien, memeriksanya untuk null dan mendapatkan objek kelas darinya 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
Karena kita bekerja dengan sebuah string, tidak akan sulit bagi kita untuk mengubah sedikit kodenya sehingga kita selalu mendapatkan tanggal dalam format yang sama pada keluarannya. Mari kita nyatakan dua konstanta - salah satunya akan sama dengan id default UTC + 00, dan yang kedua - yang ingin kita berikan kepada klien, dan tambahkan tanda centang - jika waktu server berada di zona waktu nol, maka kita akan menggantinya Z
dengan +00:00
. Hasilnya, pembuat serial kami akan terlihat seperti ini
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);
}
}
}
Total
Berkat mekanisme pegas bawaan, kami dapat secara otomatis mengonversi tanggal dan waktu dalam format yang diperlukan, tanpa panggilan metode eksplisit apa pun dalam kode.
Kode sumber lengkapnya dapat dilihat di sini