知識庫 / Java Dates RSS 訂閱

Jackson OffsetDateTime 序列化

Jackson,Java Dates
HongKong
7
09:46 PM · Dec 05 ,2025

1. 概述

本教程將介紹如何使用 Jackson 序列化 OffsetDateTime

OffsetDateTime 是一個不可變的日期時間表示形式,具有相對於 UTC/格林威治時區的時間偏移量,在 ISO-8601 日曆系統中。例如,2023-10-31T01:30+01:00 表示 2023 年 10 月 31 日凌晨 1 點 30 分的時間,相對於 UTC 時間偏移 1 小時。

Jackson 默認情況下不支持序列化 OffsetDateTime,因為它是一個 Java 8 日期時間類型。 讓我們看看如何啓用它。

2. 項目設置

讓我們從一個簡單的示例開始。我們將創建一個具有OffsetDateTime類型字段的類,並將其序列化為 JSON。

2.1. 依賴

讓我們首先將 Jackson databind 依賴添加到我們的 pom.xml 中:

<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() 方法將 JavaTimeModuleObjectMapper 註冊。我們還禁用了 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<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));
    }
}

讓我們來分析一下上述代碼中的一些重要要點:

  1. 我們創建了一個 DateTimeFormatter,並指定了我們想要使用的格式。這裏我們使用了與默認格式不同的格式。
  2. 接下來,我們通過調用 DateTimeFormatterformat() 方法來格式化日期。
  3. 最後,我們通過調用 JsonGeneratorwriteString() 方法將格式化後的日期寫入。

現在,我們可以將這個序列化器註冊到 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,其次通過定義自定義序列化器。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.