ZonedDateTime 的 Jackson 反序列化问题
Jackson deserialization issue for ZonedDateTime
我在反序列化我正在使用的服务期间使用的 class 中有以下字段。
private ZonedDateTime transactionDateTime;
我使用的服务可能 return 日期或日期时间使用模式:yyyy-MM-dd'T'HH:mm:ss.SSSZ
让我举两个例子说明服务 returns:
- 2015-11-18T18:05:38.000+0200
- 2015-11-18T00:00:00.000+0200
虽然第一个运行良好,但后者导致在反序列化过程中抛出以下异常:
java.time.format.DateTimeParseException: Text
'2015-11-18T00:00:00.000+0200' could not be parsed at index 23
我正在使用;
- Spring 引导 1.3.1
- Jackson 2.6.4(包含 JSR310 模块)
这是否需要自定义反序列化class?
您可以使用如下注释:
@JsonSerialize(using = MyCustomJsonDateSerializer.class)
或
@JsonDeserialize(using = MyCustomJsonDateDeserializer.class)
自定义 Jackson 解析日期的方式。这些自定义序列化器和反序列化器必须扩展 JsonSerializer 和 JsonDeserializer。例如:
public class MyCustomJsonDateSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date date, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(date != null ? ISODateTimeFormat.dateTime().print(new DateTime(date)) : null);
}
}
在代码的前面,我使用带有 @JsonFormat
注释的字段,但删除了它,因为我认为它仅用于序列化,就像 JavaDocs 所建议的那样。
原来我需要加回那个注释。真正的问题是第 3 方服务响应确实是错误的(它在 XML 中缺少包装元素)导致反序列化失败。错误是:
com.fasterxml.jackson.databind.JsonMappingException: Can not
instantiate value of type [simple type, class
com.foo.bar.adapter.john.model.account.UserAccount]
from String value ('2015-11-18T00:00:00.000+0200'); no single-String
constructor/factory method
字段写法如下:
@JsonFormat(pattern = Constants.DATETIME_FORMAT)
@JacksonXmlProperty(localName = "transactionDate")
private ZonedDateTime transactionDateTime;
我还必须将 @JsonRootName("transaction")
添加到该字段的 class 中,因为该对象被包装到一个集合中。
Jackson 反序列化将默认绕过时区信息并使用 ctx 时区覆盖它,所有 ISO8601 将以 UTC 结束
如果您使用 spring
,可以通过 关闭此功能
spring.jackson.deserialization.ADJUST_DATES_TO_CONTEXT_TIME_ZONE=false
我用过
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private ZonedDateTime startDate;
加上 jackson-datatype-jsr310
库,显然。
中描述了此解决方案
以下配置对我有帮助
指定日期时间模式:
public class Timestamp {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private ZonedDateTime timestamp;
}
禁止将 ZonedDateTime 转换为 UTC:
objectMapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
还有另一个问题,可能很难发现。在我的例子中,我使用 RestTemplate 从另一个微服务接收 Timestamp
。 RestTemplate 可以配置为使用 notProjectDefaultObjectMapper
,这不受默认 Spring Jackson 配置方法的影响(如应用程序属性,或 @Configuration
类 with Jackson2ObjectMapperBuilderCustomizer
bean 定义或其他方式)。所以RestTemplate的objectMapper(如果有的话)也应该配置
我在反序列化我正在使用的服务期间使用的 class 中有以下字段。
private ZonedDateTime transactionDateTime;
我使用的服务可能 return 日期或日期时间使用模式:yyyy-MM-dd'T'HH:mm:ss.SSSZ
让我举两个例子说明服务 returns:
- 2015-11-18T18:05:38.000+0200
- 2015-11-18T00:00:00.000+0200
虽然第一个运行良好,但后者导致在反序列化过程中抛出以下异常:
java.time.format.DateTimeParseException: Text '2015-11-18T00:00:00.000+0200' could not be parsed at index 23
我正在使用;
- Spring 引导 1.3.1
- Jackson 2.6.4(包含 JSR310 模块)
这是否需要自定义反序列化class?
您可以使用如下注释:
@JsonSerialize(using = MyCustomJsonDateSerializer.class)
或
@JsonDeserialize(using = MyCustomJsonDateDeserializer.class)
自定义 Jackson 解析日期的方式。这些自定义序列化器和反序列化器必须扩展 JsonSerializer 和 JsonDeserializer。例如:
public class MyCustomJsonDateSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date date, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(date != null ? ISODateTimeFormat.dateTime().print(new DateTime(date)) : null);
}
}
在代码的前面,我使用带有 @JsonFormat
注释的字段,但删除了它,因为我认为它仅用于序列化,就像 JavaDocs 所建议的那样。
原来我需要加回那个注释。真正的问题是第 3 方服务响应确实是错误的(它在 XML 中缺少包装元素)导致反序列化失败。错误是:
com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class com.foo.bar.adapter.john.model.account.UserAccount] from String value ('2015-11-18T00:00:00.000+0200'); no single-String constructor/factory method
字段写法如下:
@JsonFormat(pattern = Constants.DATETIME_FORMAT)
@JacksonXmlProperty(localName = "transactionDate")
private ZonedDateTime transactionDateTime;
我还必须将 @JsonRootName("transaction")
添加到该字段的 class 中,因为该对象被包装到一个集合中。
Jackson 反序列化将默认绕过时区信息并使用 ctx 时区覆盖它,所有 ISO8601 将以 UTC 结束
如果您使用 spring
,可以通过 关闭此功能spring.jackson.deserialization.ADJUST_DATES_TO_CONTEXT_TIME_ZONE=false
我用过
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private ZonedDateTime startDate;
加上 jackson-datatype-jsr310
库,显然。
以下配置对我有帮助
指定日期时间模式:
public class Timestamp {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private ZonedDateTime timestamp;
}
禁止将 ZonedDateTime 转换为 UTC:
objectMapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
还有另一个问题,可能很难发现。在我的例子中,我使用 RestTemplate 从另一个微服务接收 Timestamp
。 RestTemplate 可以配置为使用 notProjectDefaultObjectMapper
,这不受默认 Spring Jackson 配置方法的影响(如应用程序属性,或 @Configuration
类 with Jackson2ObjectMapperBuilderCustomizer
bean 定义或其他方式)。所以RestTemplate的objectMapper(如果有的话)也应该配置