Java 8 Time API 的 ObjectMapper 配置

Posted

技术标签:

【中文标题】Java 8 Time API 的 ObjectMapper 配置【英文标题】:ObjectMapper configuation for Java 8 Time API 【发布时间】:2018-10-03 08:14:17 【问题描述】:

我们正在从 Joda 迁移到 Java Time。目前我们在实体中使用 Joda 的DateTime。 AFAIK DateTime 相当于 Java 中的两种类型:OffsetDateTimeZonedDateTime。由于我们要将它们保存在 DB 中,因此我们将使用 OffsetDateTime(对此有何评论?)。

现在的问题是如何正确配置杰克逊的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 日期时间值的最佳方法是什么?

更新:接受的答案并不能真正解决我的问题(阅读 cmets 中的讨论)。我最终得到了比我在上面提出的更简单的代码。也请参阅我自己的答案。

【问题讨论】:

你不需要自己写,Java 8 日期和时间 API 已经有一个 Jackson 模块:github.com/FasterXML/jackson-modules-java8 更新了问题。如何使用自定义格式? 与我的问题有关:***.com/questions/37166217/… 我想是的。我们的格式是2016-05-11T17:32:20.897+00002016-05-11T17:32:20.897+00:00(没有祖鲁符号)(我们希望支持多种输入格式) 【参考方案1】:

您无需为JSR-310 类型编写自定义序列化程序和反序列化程序。 Jackson 有一个自定义模块来处理这个问题,并将为您提供所需的 serializer 和 deserializer。

首先将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 字符串表示进行序列化。如果需要自定义格式,可以使用自己的序列化器和反序列化器实现。

详情请参阅documentation。

【讨论】:

感谢您的回答。那么如何使用自定义格式呢? @Rad 大多数 JSR-310 类型将使用标准 ISO-8601 字符串表示进行序列化。如果需要自定义格式,可以使用自己的序列化器和反序列化器实现。 那么我发布的实现是唯一的方法吗? 注意 ISO-8601 的微秒部分。在序列化恰好为 0.000 的 Instant 时,我们遇到了缺少 .SSS 部分的时间戳的问题。如果这对您的客户来说是个问题,您需要自定义序列化以使用不这样做的自定义模式:“yyyy-MM-dd'T'HH:mm:ss.SSS'Z'”。 @CassioMazzochiMolin 请你看看我自己的回答***.com/a/50018489/2194119。【参考方案2】:

您可以查看这个答案,其中包含有关如何使用 java.time 类和自定义格式的大量信息:https://***.com/a/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();

【讨论】:

【参考方案3】:

好的,所以我最终得到了以下内容(代码少一点,没有具体的类):

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

【讨论】:

【参考方案4】:

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 部分了解更多信息。

【讨论】:

以上是关于Java 8 Time API 的 ObjectMapper 配置的主要内容,如果未能解决你的问题,请参考以下文章

Java之Date Time API (Java 8 新特性)

java 使用Java 8 Date and Time API(JSR 310)的示例

java 使用Java 8 Date and Time API(JSR 310)的示例

java_JDK8中新增的时间API

Java 8 Time API 的 ObjectMapper 配置

Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析