Jackson 将 ISO8601 格式的日期时间反序列化为 Java8 Instant

Posted

技术标签:

【中文标题】Jackson 将 ISO8601 格式的日期时间反序列化为 Java8 Instant【英文标题】:Jackson deserialize ISO8601 formatted date-time into Java8 Instant 【发布时间】:2016-07-15 03:14:03 【问题描述】:

我正在尝试使用 Jackson 将 ISO8601 格式的日期反序列化为 Java8 java.time.Instant。我用 ObjectMapper 注册了 JavaTimeModule,并关闭了WRITE_DATES_AS_TIMESTAMPS 设置。

但是,如果尝试反序列化 2016-03-28T19:00:00.000+01:00 它将不起作用,因为 JavaTimeModule 似乎只会反序列化使用 UTC 时区偏移格式化的日期时间(例如 2016-03-28T18:00:00.000Z)。然后我尝试像这样使用@JsonFormat 注释:

@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "UTC")

像这样:

@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = JsonFormat.DEFAULT_TIMEZONE)

但是,这些都不起作用,我得到了一个例外:

com.fasterxml.jackson.databind.JsonMappingException: Unsupported field: YearOfEra (through reference chain: org.example.Article["date"])

这意味着时区参数被忽略并且日期时间格式化程序不知道如何格式化没有时区的 Instant。

有没有办法使用 Jackson 和 JavaTimeModule 将不在 UTC 时区偏移中的 ISO8601 字符串反序列化为 Java 8 java.time.Instant 而无需编写自定义反序列化器?

【问题讨论】:

奇怪,我预计会出现此错误Failed to parse Date value '2016-03-28T19:00:00.000+01:00' (format: "yyyy-MM-dd'T'HH:mm:ss.SSSZ"): Unparseable date: "2016-03-28T19:00:00.000+01:00",在尝试了您的代码后,我在这两种情况下都遇到了这种错误。格式不匹配...你试过没有任何注释吗? 或使用此模式:yyyy-MM-dd'T'HH:mm:ss.SSSXXX(参见:docs.oracle.com/javase/7/docs/api/java/text/…) 在没有任何注释的情况下它可以工作,但前提是偏移量指定为 Z。它不适用于 +0100 或 +01:00。我尝试了您的格式,但仍然得到 Unsupported field: YearOfEra 异常,表明反序列化器的格式化程序未配置时区(出于某种原因,java 8 fromatter 需要 TZ,即使在字符串中指定了偏移量并且 Instant 已完全定义) . 您使用的是哪个版本的jackson / jackson-datatype-jsr310? 杰克逊 2.6.5 和杰克逊-datatype-jsr310 2.6.5 【参考方案1】:

Jackson 可以全局配置(不带注释)以接受带或不带冒号的时间戳

ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true));

自 2.11 版起,默认杰克逊时区格式已从“+0000”更改为“+00:00”。根据 ISO-8601,这两种格式都有效。

【讨论】:

它们并非都有效。一种是基本格式,另一种是扩展的 ISO-8601 格式。你不能混合和匹配它们。【参考方案2】:

您需要在模型类中通过XXX 设置明确的时区:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")

(见:https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

【讨论】:

使用建议的格式并在正确的位置放置注释解决了这个问题。 Date 是多年前被现代 java.time 类取代的可怕遗留日期时间类的一部分。具体来说,java.util.Date 被替换为 java.time.Instant,本问题中请求的类。 由于 Guss 的回答,我为什么使用 Cannot construct instance of java.text.SimpleDateFormat, problem: Illegal pattern character 'T' 将类型更改为 Date 并保持 @JsonFormat(shape = JsonFormat.Shape.STRING) 帮助了我 谢谢,这对我帮助很大。我建议将毫秒部分设为可选:yyyy-MM-dd'T'HH:mm:ss[.SSS]XXX【参考方案3】:

在 Jackson 2.9.8(我写这篇文章时的当前版本)中,最好使用 Instant 而不是 Date。

你必须添加一个依赖:

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

另外,注册模块并将 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 配置为 false。

new ObjectMapper()
                .findAndRegisterModules()
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

在此处了解有关 Jackson for Java8 的更多信息:https://github.com/FasterXML/jackson-modules-java8

【讨论】:

这个调整整个配置!【参考方案4】:

如果您想将Date 对象序列化为 ISO-8601,则根本不需要指定模式 - ISO-8601 是默认模式。 JsonFormat Java doc 中提到过:

常见用途包括在替代表示之间进行选择——例如,是否将日期序列化为数字(Java 时间戳)或 字符串(如 ISO-8601 兼容时间值)——以及就像使用 pattern() 属性配置确切的细节一样。

[强调我的]您应该从上面的文本中理解指定shape = STRING 将意味着ISO-8601 格式,但您可以使用pattern 属性选择其他内容。

根据我的经验,这始终是 UTC 日期格式(时区呈现为 +0000),这可能是我的 VM 中的默认时区(即使我的操作系统时钟未设置为 UTC) .

【讨论】:

我认为这个问题是关于反序列化,而不是序列化。不过很高兴知道! 反序列化的工作方式相同 - @JsonFormat 控制两者。我两种方式都这样使用。 迄今为止最好的答案恕我直言。简单而标准。 这在反序列化非 UTC ISO 日期时间时不起作用。我测试过了。【参考方案5】:

格式“Z”不适用于“+01:00”,因为这是一种不同的模式。 JsonFormat 使用 SimpleDateFormat 模式。 大写中的“Z”仅表示严格的 RFC 822。您必须使用如下语法:“+0100”,不带冒号。

见:ISO 8601:2004、SimpleDateFormat patterns

【讨论】:

以上是关于Jackson 将 ISO8601 格式的日期时间反序列化为 Java8 Instant的主要内容,如果未能解决你的问题,请参考以下文章

一种将当前日期时间转换为 ISO 8601 格式的优雅方法 [重复]

Jackson , java.time , ISO 8601 , 无毫秒序列化

使用Jackson时转换JSON时,日期格式设置

如何将 Excel 中的日期转换为 ISO 8601 格式

如何使用 PHP 将日期显示为 iso 8601 格式

将 Luxon 日期格式化为 ISO8601 基本格式