Java 8时间API的ObjectMapper配置

12

我们正在从Joda迁移到Java Time。目前我们在实体中使用Joda的DateTime。据我所知,DateTime在Java中相当于两种类型:OffsetDateTimeZonedDateTime。由于我们将把它们持久化到数据库中,因此我们将使用OffsetDateTime(对此有什么评论吗?)。

现在的问题是如何正确配置Jackson的ObjectMapper。 我在网上找到的所有示例都是关于本地类型的,这些类型Jackson已经提供了de/serializer实现(例如LocalDateTimeLocalDateTimeSerializerLocalDateTimeDeserializer)。

最终,我成功地做到了这样:

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来进行Java 8日期时间值的序列化/反序列化?

更新:被接受的答案并没有真正解决我的问题(请阅读评论中的讨论)。我最终使用了比我在上面提出的代码更简单的代码。您也可以看看我的回答。


3
不需要自己编写代码,Java 8日期和时间API已经有了一个Jackson模块:https://github.com/FasterXML/jackson-modules-java8 - Jesper
更新了问题。如何使用自定义格式? - Rad
1
与我的问题相关:https://dev59.com/GFoU5IYBdhLWcg3wu4ri - Andrew Tobilko
我想是这样的。我们的格式是2016-05-11T17:32:20.897+00002016-05-11T17:32:20.897+00:00(没有Zulu符号)(我们希望支持多种输入格式)。 - Rad
4个回答

18

您不需要为JSR-310类型编写自定义序列化程序和反序列化程序。Jackson有一个自定义模块来处理这个,会为您提供所需的serializerdeserializer

首先将jackson-datatype-jsr310工件添加到您的依赖项中:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.9</version>
</dependency>

然后在您的ObjectMapper中注册JavaTimeModule模块:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

大多数JSR-310类型将使用标准的ISO-8601字符串表示进行序列化。如果您需要自定义格式,可以使用自己的序列化器和反序列化器实现。
有关详细信息,请参见文档

谢谢您的回答。但是如何使用自定义格式? - Rad
@Rad 大多数 JSR-310 类型将使用标准 ISO-8601 字符串表示进行序列化。如果您需要自定义格式,可以使用自己的序列化和反序列化实现。 - cassiomolin
那么我发布的实现方式是唯一可行的吗? - Rad
@Rad 是的,如果您想要自定义格式,那就是正确的方法。 - cassiomolin
4
请注意 ISO-8601 中的微秒部分。我们在序列化瞬间时遇到了时间戳缺少 .SSS 部分的问题,其中它恰好是 .000。如果这对您的客户造成了问题,您需要自定义序列化以使用不会出现此问题的自定义模式:"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"。 - Jilles van Gurp
1
@CassioMazzochiMolin,您能否看一下我的回答 https://dev59.com/c1UL5IYBdhLWcg3wqpo7#50018489 。 - Rad

7

好的,所以最终我得到了以下代码(精简了一些代码,没有具体的类):

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) {};
}

1
您可以查看这个答案,其中包含有关如何使用java.time类和自定义格式的大量信息:https://dev59.com/11YO5IYBdhLWcg3wKObt#46263957 为了解析"+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();

0

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部分获取更多信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接