OffsetDateTime 序列化与 Jackson
最后更新:2024年5月11日
1. 概述
在本教程中,我们将探讨如何使用 Jackson 序列化 OffsetDateTime。
OffsetDateTime 是 ISO-8601 日历系统中,相对于 UTC/格林威治时间的日期时间的不变表示。 例如,2023-10-31T01:30+01:00 表示 2023 年 10 月 31 日的最后分钟,相对于 UTC 时间的偏移量为一小时。
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>
现在,我们可以在序列化对象之前使用 registerModule() 方法将此模块注册到 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. 自定义序列化器
我们可以通过创建一个扩展 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() 方法来格式化日期。
- 最后,我们通过调用 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,其次是通过定义自定义序列化器。
支持本文的代码可在 GitHub 上获取。 一旦你以 Baeldung Pro 会员 身份登录,就开始学习并在项目上进行编码。















