1. 概述
在这个教程中,我们将探讨如何使用 Jackson 序列化 OffsetDateTime
类型。OffsetDateTime
是 ISO-8601 日历系统中与格林威治标准时间(UTC)有偏移的日期时间的不可变表示。例如,2023-10-31T01:30+01:00 表示 2023 年 10 月 31 日的最后一分钟,与 UTC 有一个小时的时间差。
Jackson 默认情况下不支持序列化 OffsetDateTime
,因为它是 Java 8 的日期时间类型。让我们看看如何启用它。
2. 项目设置
2.1. 依赖项
首先,我们需要在 pom.xml
中添加 Jackson 数据绑定 依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.1</version>
</dependency>
2.2. 代码示例
首先,定义一个包含 OffsetDateTime
类型字段的类,并尝试将其序列化为 JSON:
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()
方法注册了 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<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
,用于指定我们想要使用的格式。这里我们使用了与默认不同的格式。 - 接下来,我们调用
DateTimeFormatter
的format()
方法格式化日期。 - 最后,通过调用
JsonGenerator
的writeString()
方法将格式化的日期写入。
现在,我们可以在序列化对象之前使用 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
。我们介绍了两种解决方案来处理 OffsetDateTime
的默认序列化问题:一是使用 JavaTimeModule
,二是定义自定义序列化器。
如往常一样,本文的代码示例可以在 GitHub 上找到。