【问题标题】:Java date pattern for deserializing date with variable number of fractional seconds?用于反序列化具有可变小数秒数的日期的Java日期模式?
【发布时间】:2017-07-03 10:34:45
【问题描述】:

我在 Spring Boot 应用程序中使用了一个 Java 类来反序列化来自具有如下字段的请求的 json 对象:

@JsonFormat(pattern="yyyy-MM-dd'T'HH:mm:ss[.SSS]XXX", timezone = "UTC")
Instant timestamp;

它使用 Jackson V2.6.6 @JsonFormat 注释来反序列化来自网络的 JSON 中的“时间戳”字段。

我看到的是,当值类似于:

“2017-01-09T21:49:26.70Z”

没有问题。

但是,如果该值包含单个小数位第二个小数位,或者多于三个,例如:

“2017-01-09T21:49:26.7Z”或“2017-01-09T21:49:26.7000Z”

然后我会得到以下错误:

Caused by: java.time.format.DateTimeParseException: Text '2017-01-09T21:49:26.7Z' could not be parsed at index 19
    at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949) ~[?:1.8.0_65]
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1777) ~[?:1.8.0_65]
    at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:150) ~[jackson-datatype-jsr310-2.6.6.jar:2.6.6]
    at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:45) ~[jackson-datatype-jsr310-2.6.6.jar:2.6.6]
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:520) ~[jackson-databind-2.6.6.jar:2.6.6]
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:101) ~[jackson-databind-2.6.6.jar:2.6.6]
    at com.fasterxml.jackson.module.afterburner.deser.SuperSonicBeanDeserializer.deserialize(SuperSonicBeanDeserializer.java:156) ~[jackson-module-afterburner-2.6.6.jar:2.6.6]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3736) ~[jackson-databind-2.6.6.jar:2.6.6]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2764) ~[jackson-databind-2.6.6.jar:2.6.6]

我可以做些什么来修复这个模式,以便它可以灵活地接受小数点后的小数位数?我知道Java日期格式中有一个宽松的概念:https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html

但我不确定如何将对象映射器设置为宽松模式。

【问题讨论】:

  • 如果没有任何效果,您可以将其读取为String 并使用正则表达式对其进行解析

标签: java parsing datetime jackson deserialization


【解决方案1】:

更新您的 Java 实现

只是猜测……

我怀疑您可能在 Java 8 的早期版本中遇到了一些 java.time 错误。有几个与解析相关的错误,一些在 Java 8 的后续更新中修复,一些在 Java 9 中修复。

我猜你的错误文本中的 1.8.0_65 字符串是指 Java 8 Update 65。目前是 Oracle implementation is at Update 121

什么格式化程序?

我不知道Jackson。我想知道他们在调用什么确切的DateTimeFormatter。我在您的错误引用中没有看到该信息。

您可以省略定义模式并查看 Jackson 的默认行为吗?

Instant.parse没问题

在运行 macOS Sierra 10.12.3 和 NetBeans 8.2 的 MacBook Pro(Retina,15 英寸,2013 年末)上的 Java 8 Update 121 中调用 Instant.parse 解析字符串时,我没有看到这样的问题.

System.out.println ( "Java vendor: " + System.getProperty ( "java.vendor" ) );
System.out.println ( "Java version: " + System.getProperty ( "java.version" ) );

List<String> strings = new ArrayList<> ();
strings.add ( "2017-01-09T21:49:26.7Z" );
strings.add ( "2017-01-09T21:49:26.70Z" );
strings.add ( "2017-01-09T21:49:26.700Z" );
strings.add ( "2017-01-09T21:49:26.7000Z" );
strings.add ( "2017-01-09T21:49:26.70000Z" );

for ( String string : strings ) {
    try {
        Instant instant = Instant.parse ( string );
        System.out.println ( "GOOD - string: " + string + " parsed: " + instant );
    } catch ( DateTimeParseException e ) {
        System.out.println ( "ERROR - Failed to parse string: " + string );
    }
}
System.out.println ( "Done." );

运行时。

Java vendor: Oracle Corporation
Java version: 1.8.0_121
GOOD - string: 2017-01-09T21:49:26.7Z parsed: 2017-01-09T21:49:26.700Z
GOOD - string: 2017-01-09T21:49:26.70Z parsed: 2017-01-09T21:49:26.700Z
GOOD - string: 2017-01-09T21:49:26.700Z parsed: 2017-01-09T21:49:26.700Z
GOOD - string: 2017-01-09T21:49:26.7000Z parsed: 2017-01-09T21:49:26.700Z
GOOD - string: 2017-01-09T21:49:26.70000Z parsed: 2017-01-09T21:49:26.700Z

您还可以毫无问题地看到同样的code run live at IdeOne.com,运行 Oracle 实现的 Java 8 Update 121。

您的DateTimeFormatter.ofPattern 有问题

我也尝试了您的格式化模式。它适用于.70.700,但不适用于其他人。

DateTimeFormatter f = DateTimeFormatter.ofPattern ( "yyyy-MM-dd'T'HH:mm:ss[.SSS]XXX" , Locale.US );
for ( String string : strings ) {
    try {
        OffsetDateTime odt = OffsetDateTime.parse ( string , f );
        System.out.println ( "GOOD - string: " + string + " parsed: " + odt );
    } catch ( DateTimeParseException e ) {
        System.out.println ( "ERROR - Failed to parse string: " + string );
    }
}
System.out.println ( "Done with 'DateTimeFormatter.ofPattern'." );
ERROR - Failed to parse string: 2017-01-09T21:49:26.7Z
GOOD - string: 2017-01-09T21:49:26.70Z parsed: 2017-01-09T21:49:26.700Z
GOOD - string: 2017-01-09T21:49:26.700Z parsed: 2017-01-09T21:49:26.700Z
ERROR - Failed to parse string: 2017-01-09T21:49:26.7000Z
ERROR - Failed to parse string: 2017-01-09T21:49:26.70000Z

ISO 8601

仅供参考,2017-01-09T21:49:26.70Z 等字符串的格式在ISO 8601 标准中定义。 java.time 类在解析或生成表示日期时间值的字符串时默认使用这些标准 ISO 8601 格式。

该特定格式使用T 将年-月-日部分与时-分-秒部分分开。 ZZulu 的缩写,意思是 UTC

您可以通过searching StackOverflow查看更多信息。

【讨论】:

  • 刚刚更新到 1.8.0.121,它仍然在“2017-01-09T21:49:26.7Z”上失败,使用模式:“yyyy-MM-dd'T'HH:mm:ss[ .SSS]XXX"
  • @AdamBronfin 完全摆脱@JsonFormat。如果你这样做,Jackson 将使用 DateTimeFormatter.ISO_INSTANT,这就是 Instant#parse 使用的,这也是 Basil 在这里建议的。
  • @SotiriosDelimanolis 我同意你作为实验的建议。但我添加了更多代码,表明他的格式化模式应该和DateTimeFormatter.ISO_INSTANT 一样有效。
  • @AdamBronfin 您是否尝试过从我的 Answer 或有效的 IdeOne.com 代码中复制粘贴代码以查看它们是否适用于您的设置?
  • 您实际上并没有使用您创建的DateTimeFormatter
猜你喜欢
  • 2021-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多