使用 Jackson 進行 OffsetDateTime 序列化
一、概述
在本教程中,我們將探索如何使用 Jackson 序列化OffsetDateTime
。
OffsetDateTime
是日期時間的不可變表示,與 ISO-8601 日曆系統中的 UTC/Greenwich 具有偏移量。例如,2023-10-31T01:30+01:00 表示 2023 年 10 月 31 日最後一分鐘的日期時間,與 UTC 偏移一小時。
默認情況下,Jackson 不會序列化OffsetDateTime
,因為它是 Java 8 日期時間類型。讓我們看看如何啟用它。
2.項目設置
讓我們從一個簡單的例子開始。我們將創建一個具有OffsetDateTime
類型字段的類,並將其序列化為 JSON。
2.1.依賴性
讓我們從將Jackson 數據綁定依賴項添加到我們的pom.xml
開始:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.1</version> </dependency>
2.2.代碼示例
首先,讓我們定義要序列化的類:
`public class User {
private OffsetDateTime createdAt;
// constructors, getters and setters
}`
接下來,我們將創建一個User
對象序列化為 JSON 的方法:
String serializeUser() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
User user = new User();
user.setCreatedAt(OffsetDateTime.parse("2021-09-30T15:30:00+01:00"));
return objectMapper.writeValueAsString(user);
}
如果我們調用上面的方法,我們會得到以下錯誤:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.OffsetDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.baeldung.offsetdatetime.User["createdAt"])
正如我們所見,Jackson 默認不支持序列化OffsetDateTime
。讓我們看看解決這個問題的方法。
3.註冊JavaTimeModule
正如錯誤消息所建議的那樣,Jackson 提供了一個名為JavaTimeModule
的模塊,我們可以使用它以正確的格式序列化OffsetDateTime
。
首先,我們需要在pom.xml
中包含[jackson-datatype-jsr310](https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310)
依賴項:
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.14.1</version> </dependency>
現在,我們可以在序列化對象之前用ObjectMapper
註冊這個模塊:
`String serializeUser() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.registerModule(new JavaTimeModule());
User user = new User();
user.setCreatedAt(OffsetDateTime.parse("2021-09-30T15:30:00+01:00"));
return objectMapper.writeValueAsString(user);
}`
我們使用registerModule()
方法向ObjectMapper
註冊了JavaTimeModule
。我們還禁用了SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
功能,以獲取與輸入格式相同的日期,而不是時間戳。
當我們再次調用該方法時,錯誤消失了,我們在輸出中得到了序列化日期。我們可以使用 JUnit 測試來測試它:
@Test void givenUser_whenSerialized_thenCreatedDateIsSerialized() throws JsonProcessingException { Assertions.assertEquals("{\"createdAt\":\"2021-09-30T15:30:00+01:00\"}", Main.serializeUser()); }
4.自定義序列化和反序列化
解決此問題的另一種方法是為OffsetDateTime
創建自定義序列化程序。如果我們還想自定義日期格式,這將是首選方式。
4.1.自定義序列化程序
我們可以通過創建一個擴展 Jackson 的JsonSerializer
類並覆蓋serialize()
方法的類來做到這一點:
`public class OffsetDateTimeSerializer extends JsonSerializer
private static final DateTimeFormatter DATE_TIME_FORMATTER
= DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss XXX");
@Override
public void serialize(OffsetDateTime value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
if (value == null) {
throw new IOException("OffsetDateTime argument is null.");
}
jsonGenerator.writeString(DATE_TIME_FORMATTER.format(value));
}
}`
讓我們看一下上面代碼的一些要點:
- 我們用我們想要使用的格式創建了一個
DateTimeFormatter
。這裡我們使用了一種不同於默認格式的格式。 - 接下來,我們通過調用
DateTimeFormatter
上的format()
方法來格式化日期。 - 最後,我們通過調用
writeString()
方法將格式化的日期寫入JsonGenerator
。
現在我們可以在序列化對象之前用ObjectMapper
註冊這個序列化器:
`String customSerialize() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new SimpleModule().addSerializer(OffsetDateTime.class, new OffsetDateTimeSerializer()));
User user = new User();
user.setCreatedAt(OffsetDateTime.parse("2021-09-30T15:30:00+01:00"));
return objectMapper.writeValueAsString(user);
}`
我們現在可以測試我們是否以序列化程序中指定的格式獲取日期:
@Test void givenUser_whenCustomSerialized_thenCreatedDateIsSerialized() throws JsonProcessingException { Assertions.assertEquals("{\"createdAt\":\"30-09-2021 15:30:00 +01:00\"}", Main.customSerialize()); }
4.2.自定義解串器
由於我們已經創建了一個自定義序列化器,我們還需要創建一個自定義反序列化器來反序列化 JSON 字符串中的日期。如果我們不這樣做,我們將再次得到相同的InvalidDefinitionException
。
我們可以通過創建一個擴展JsonDeserializer
類並覆蓋deserialize()
方法來做到這一點:
`public class OffsetDateTimeDeserializer extends JsonDeserializer
private static final DateTimeFormatter DATE_TIME_FORMATTER
= DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss XXX");
@Override
public OffsetDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException {
String dateAsString = jsonParser.getText();
if (dateAsString == null) {
throw new IOException("OffsetDateTime argument is null.");
}
return OffsetDateTime.parse(dateAsString, DATE_TIME_FORMATTER);
}
}`
與序列化器類似,我們創建了一個DateTimeFormatter
,它具有我們想要使用的格式。最後,我們將格式化程序傳遞給parse()
方法以獲取OffsetDateTime
對象返回值。
我們可以在任何想要反序列化對象的地方使用ObjectMapper
註冊這個反序列化器:
`String customDeserialize() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new SimpleModule().addDeserializer(OffsetDateTime.class, new OffsetDateTimeDeserializer()));
String json = "{"createdAt":"30-09-2021 15:30:00 +01:00"}";
User user = objectMapper.readValue(json, User.class);
return returnedUser.getCreatedAt().toString();
}`
我們在輸出中獲取默認OffsetDateTime
格式的日期:
@Test
void givenUser_whenCustomDeserialized_thenCreatedDateIsDeserialized() throws JsonProcessingException {
Assertions.assertEquals("2021-09-30T15:30+01:00", Main.customDeserialize());
}
5.結論
在本文中,我們了解瞭如何使用 Jackson 序列化和反序列化OffsetDateTime
。我們看到了兩種使用 Jackson 修復OffsetDateTime
的默認序列化的解決方案——首先使用JavaTimeModule
,其次通過定義自定義序列化程序。
與往常一樣,本文中的代碼示例可以在 GitHub 上找到。