1. 概述
在本教程中,我們將探討如何使用 Jackson 序列化 OffsetDateTime。
OffsetDateTime 是一個不可變的日期時間表示形式,具有從 UTC/格林威治時區的時間偏移量,在 ISO-8601 日曆系統中。 例如,2023-10-31T01:30+01:00 表示 2023 年 10 月 31 日凌晨 1 點 30 分的時間,偏移量為 UTC 時間的一小時。
Jackson 默認情況下不序列化 OffsetDateTime,因為它是一個 Java 8 日期時間類型。 讓我們看看如何啓用它。
2. 項目設置
我們先從一個簡單的例子開始。我們將創建一個字段類型為 OffsetDateTime 的類,並將其序列化為 JSON。2.1. 依賴項
首先,我們添加到我們的 pom.xml 中的 Jackson databind 依賴項:<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</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 依賴項:
<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() 方法將 JavaTimeModule 註冊到 ObjectMapper 中。我們還禁用了 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. 自定義序列化器
我們可以通過創建一個繼承 JsonSerializer 類的類並覆蓋 serialize() 方法來實現:
public class OffsetDateTimeSerializer extends JsonSerializer<OffsetDateTime> {
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,其中包含我們想要使用的格式。 我們在此處使用了不同的格式,而不是默認格式。
- 接下來,我們通過調用 format() 方法來格式化日期,該方法在 DateTimeFormatter 對象上調用。
- 最後,我們通過調用 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<OffsetDateTime> {
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,其次通過定義自定義序列化器。