Java 8 次 API 的 ObjectMapper 配置
ObjectMapper configuation for Java 8 Time API
我们正在从 Joda 迁移到 Java Time。目前我们在我们的实体中使用 Joda 的 DateTime
。 AFAIK DateTime
相当于 Java 中的两种类型:OffsetDateTime
和 ZonedDateTime
。由于我们要将它们保存在数据库中,因此我们将使用 OffsetDateTime
(对此有何评论?)。
现在的问题是如何正确配置 Jackson 的 ObjectMapper
。
我在网上找到的所有示例都是关于 Jackson 已经提供 de/serializer 实现的本地类型(例如 LocalDateTime
、LocalDateTimeSerializer
和 LocalDateTimeDeserializer
)。
我终于设法做了这样的事情:
public class OffsetDateTimeSerializer extends StdSerializer<OffsetDateTime> {
private final DateTimeFormatter formatter; // We need custom format!
public OffsetDateTimeSerializer(DateTimeFormatter formatter) {
super(OffsetDateTime.class);
this.formatter = formatter;
}
@Override
public void serialize(OffsetDateTime value, JsonGenerator generator, SerializerProvider provider) throws IOException {
generator.writeString(value.format(formatter));
}
}
和
public class OffsetDateTimeDeserializer extends StdDeserializer<OffsetDateTime> {
private final DateTimeFormatter formatter; // We need custom format!
public OffsetDateTimeDeserializer(DateTimeFormatter formatter) {
super(OffsetDateTime.class);
this.formatter = formatter;
}
@Override
public OffsetDateTime deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
return OffsetDateTime.parse(parser.readValueAs(String.class), formatter);
}
}
现在我的问题是将 Jackson 的 ObjectMapper
配置为 de/serialize Java 8 个日期时间值的最佳方法是什么?
更新:接受的答案并没有真正解决我的问题(阅读评论中的讨论)。我最终得到的代码比我在上面提出的要简单一些。也可以看看我自己的回答。
您不需要为 JSR-310 types. Jackson has a custom module to handle that and will provide you with the serializer and deserializer 编写您需要的自定义序列化程序和反序列化程序。
首先将 jackson-datatype-jsr310
工件添加到您的依赖项中:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9</version>
</dependency>
然后注册JavaTimeModule
module in your ObjectMapper
:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
大多数 JSR-310 类型将使用标准的 ISO-8601 字符串表示形式进行序列化。如果您需要自定义格式,您可以使用自己的序列化器和反序列化器实现。
详情见documentation。
您可以查看此答案,其中包含大量有关如何使用 java.time 类 和自定义格式的信息:
要解析“+00:00”和“+0000”,您可以使用带有可选部分的DateTimeFormatterBuilder
:
DateTimeFormatter f = new DateTimeFormatterBuilder()
// date and time fields
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// optional offset in format hh:mm
.optionalStart()
.appendOffset("+HH:MM", "+00:00")
.optionalEnd()
// optional offset in format hhmm
.optionalStart()
.appendOffset("+HHMM", "+0000")
.optionalEnd()
.toFormatter();
好的,所以我得到了以下结果(代码少了一点,没有具体的类):
private JavaTimeModule newJavaTimeModule() {
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(LocalDate.class, new LocalDateSerializer(DEFAULT_LOCAL_DATE_FORMATTER));
module.addDeserializer(LocalDate.class, new LocalDateDeserializer(DEFAULT_LOCAL_DATE_FORMATTER));
module.addSerializer(OffsetDateTime.class, offsetDateTimeSerializer());
module.addDeserializer(OffsetDateTime.class, offsetDateTimeDeserializer());
return module;
}
private StdSerializer<OffsetDateTime> offsetDateTimeSerializer(DateTimeFormatter formatter) {
return new OffsetDateTimeSerializer(OffsetDateTimeSerializer.INSTANCE, false, formatter) {};
}
private StdDeserializer<OffsetDateTime> offsetDateTimeDeserializer(DateTimeFormatter formatter) {
return new InstantDeserializer<OffsetDateTime>(InstantDeserializer.OFFSET_DATE_TIME, formatter) {};
}
Spring boot 为 Jackson 进行依赖管理。 2.6以后,jsr310模块可以自动管理,所以你只需要使用Jackson 2.6+并添加模块,可以在com.fasterxml.jackson.datatype:jackson-datatype-jsr310
.
如果您有权访问 ObjectMapper
,例如在单元测试中,您可以注册 JavaTimeModule
。如果你不这样做,就像 JSON 作为 @RequestBody
出现时发生的情况,你必须在 application.properties:
中配置
spring.jackson.serialization.write-dates-as-timestamps=false
并在 JSON 中指定日期格式,例如:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXXX")
private OffsetDateTime transactionDateTime;
并且字符串将被正确解析。该格式使用 SimpleDateFormat
中指定的字母。
注意最后的X
个数;不同的长度代表不同形式的区域偏移。阅读 Java API SimpleDateFormat
部分以获取更多信息。
我们正在从 Joda 迁移到 Java Time。目前我们在我们的实体中使用 Joda 的 DateTime
。 AFAIK DateTime
相当于 Java 中的两种类型:OffsetDateTime
和 ZonedDateTime
。由于我们要将它们保存在数据库中,因此我们将使用 OffsetDateTime
(对此有何评论?)。
现在的问题是如何正确配置 Jackson 的 ObjectMapper
。
我在网上找到的所有示例都是关于 Jackson 已经提供 de/serializer 实现的本地类型(例如 LocalDateTime
、LocalDateTimeSerializer
和 LocalDateTimeDeserializer
)。
我终于设法做了这样的事情:
public class OffsetDateTimeSerializer extends StdSerializer<OffsetDateTime> {
private final DateTimeFormatter formatter; // We need custom format!
public OffsetDateTimeSerializer(DateTimeFormatter formatter) {
super(OffsetDateTime.class);
this.formatter = formatter;
}
@Override
public void serialize(OffsetDateTime value, JsonGenerator generator, SerializerProvider provider) throws IOException {
generator.writeString(value.format(formatter));
}
}
和
public class OffsetDateTimeDeserializer extends StdDeserializer<OffsetDateTime> {
private final DateTimeFormatter formatter; // We need custom format!
public OffsetDateTimeDeserializer(DateTimeFormatter formatter) {
super(OffsetDateTime.class);
this.formatter = formatter;
}
@Override
public OffsetDateTime deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
return OffsetDateTime.parse(parser.readValueAs(String.class), formatter);
}
}
现在我的问题是将 Jackson 的 ObjectMapper
配置为 de/serialize Java 8 个日期时间值的最佳方法是什么?
更新:接受的答案并没有真正解决我的问题(阅读评论中的讨论)。我最终得到的代码比我在上面提出的要简单一些。也可以看看我自己的回答。
您不需要为 JSR-310 types. Jackson has a custom module to handle that and will provide you with the serializer and deserializer 编写您需要的自定义序列化程序和反序列化程序。
首先将 jackson-datatype-jsr310
工件添加到您的依赖项中:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9</version>
</dependency>
然后注册JavaTimeModule
module in your ObjectMapper
:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
大多数 JSR-310 类型将使用标准的 ISO-8601 字符串表示形式进行序列化。如果您需要自定义格式,您可以使用自己的序列化器和反序列化器实现。
详情见documentation。
您可以查看此答案,其中包含大量有关如何使用 java.time 类 和自定义格式的信息:
要解析“+00:00”和“+0000”,您可以使用带有可选部分的DateTimeFormatterBuilder
:
DateTimeFormatter f = new DateTimeFormatterBuilder()
// date and time fields
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// optional offset in format hh:mm
.optionalStart()
.appendOffset("+HH:MM", "+00:00")
.optionalEnd()
// optional offset in format hhmm
.optionalStart()
.appendOffset("+HHMM", "+0000")
.optionalEnd()
.toFormatter();
好的,所以我得到了以下结果(代码少了一点,没有具体的类):
private JavaTimeModule newJavaTimeModule() {
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(LocalDate.class, new LocalDateSerializer(DEFAULT_LOCAL_DATE_FORMATTER));
module.addDeserializer(LocalDate.class, new LocalDateDeserializer(DEFAULT_LOCAL_DATE_FORMATTER));
module.addSerializer(OffsetDateTime.class, offsetDateTimeSerializer());
module.addDeserializer(OffsetDateTime.class, offsetDateTimeDeserializer());
return module;
}
private StdSerializer<OffsetDateTime> offsetDateTimeSerializer(DateTimeFormatter formatter) {
return new OffsetDateTimeSerializer(OffsetDateTimeSerializer.INSTANCE, false, formatter) {};
}
private StdDeserializer<OffsetDateTime> offsetDateTimeDeserializer(DateTimeFormatter formatter) {
return new InstantDeserializer<OffsetDateTime>(InstantDeserializer.OFFSET_DATE_TIME, formatter) {};
}
Spring boot 为 Jackson 进行依赖管理。 2.6以后,jsr310模块可以自动管理,所以你只需要使用Jackson 2.6+并添加模块,可以在com.fasterxml.jackson.datatype:jackson-datatype-jsr310
.
如果您有权访问 ObjectMapper
,例如在单元测试中,您可以注册 JavaTimeModule
。如果你不这样做,就像 JSON 作为 @RequestBody
出现时发生的情况,你必须在 application.properties:
spring.jackson.serialization.write-dates-as-timestamps=false
并在 JSON 中指定日期格式,例如:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXXX")
private OffsetDateTime transactionDateTime;
并且字符串将被正确解析。该格式使用 SimpleDateFormat
中指定的字母。
注意最后的X
个数;不同的长度代表不同形式的区域偏移。阅读 Java API SimpleDateFormat
部分以获取更多信息。