我正在像这样解析ZonedDateTime:
@JsonSerialize(using = ZonedDateTimeSerializer.class)
private ZonedDateTime expirationDateTime;
我需要能够正确反序列化这个日期.但是,杰克逊没有为此提供反序列化器:
com.fasterxml.jackson.datatype.jsr310.deser
有什么原因会丢失它吗?最常见的解决方法是什么?
更新:
这是我的情况:
我这样创建ZonedDateTime:
ZonedDateTime.of(2017, 1, 1, 1, 1, 1, 1, ZoneOffset.UTC)
然后我序列化包含日期的对象,如下所示:
public static String toJSON(Object o) {
ObjectMapper objectMapper = new ObjectMapper();
StringWriter sWriter = new StringWriter();
try {
JsonGenerator jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(sWriter);
objectMapper.writeValue(jsonGenerator, o);
return sWriter.toString();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
当我尝试将其发送到Spring MVC Controller时:
mockMvc.perform(post("/endpoint/")
.content(toJSON(myObject))
.contentType(APPLICATION_JSON))
.andExpect(status().isOk());
控制器内部的日期对象不同.
之前:2017-01-01T01:01:01.000000001Z
之后:2017-01-01T01:01:01.000000001Z [UTC]
解决方法:
2个值2017-01-01T01:01:01.000000001Z和2017-01-01T01:01:01.000000001Z [UTC]实际上表示同一时刻,因此它们是等效的,可以毫无问题地使用(至少应该有没问题,因为它们代表同一时刻.
唯一的细节是,由于某种原因,杰克逊在反序列化时将ZoneId值设置为“ UTC”,在这种情况下,这是多余的(Z已经告知偏移量为“ UTC”).但这不应该影响日期值本身.
摆脱此[UTC]部分的一种非常简单的方法是将该对象转换为OffsetDateTime(这样,它将保持Z偏移并且不使用[UTC]区域),然后再次返回ZonedDateTime:
ZonedDateTime z = // object with 2017-01-01T01:01:01.000000001Z[UTC] value
z = z.toOffsetDateTime().toZonedDateTime();
System.out.println(z); // 2017-01-01T01:01:01.000000001Z
之后,z变量的值将为2017-01-01T01:01:01.000000001Z(不含[UTC]部分).
但这当然不是理想的,因为您必须在所有日期中手动进行操作.更好的方法是编写一个自定义反序列化器(通过扩展com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer),使其在UTC时不设置时区:
public class CustomZonedDateTimeDeserializer extends InstantDeserializer<ZonedDateTime> {
public CustomZonedDateTimeDeserializer() {
// most parameters are the same used by InstantDeserializer
super(ZonedDateTime.class,
DateTimeFormatter.ISO_ZONED_DATE_TIME,
ZonedDateTime::from,
// when zone id is "UTC", use the ZoneOffset.UTC constant instead of the zoneId object
a -> ZonedDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId.getId().equals("UTC") ? ZoneOffset.UTC : a.zoneId),
// when zone id is "UTC", use the ZoneOffset.UTC constant instead of the zoneId object
a -> ZonedDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId.getId().equals("UTC") ? ZoneOffset.UTC : a.zoneId),
// the same is equals to InstantDeserializer
ZonedDateTime::withZoneSameInstant, false);
}
}
然后,您必须注册该反序列化器.如果使用ObjectMapper,则需要将其添加到JavaTimeModule中:
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
// add my custom deserializer (this will affect all ZonedDateTime deserialization)
module.addDeserializer(ZonedDateTime.class, new CustomZonedDateTimeDeserializer());
objectMapper.registerModule(module);
如果您在Spring中进行配置,则配置将如下所示(未经测试):
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean" id="pnxObjectMapper">
<property name="deserializersByType">
<map key-type="java.lang.Class">
<entry>
<key>
<value>java.time.ZonedDateTime</value>
</key>
<bean class="your.app.CustomZonedDateTimeDeserializer">
</bean>
</entry>
</map>
</property>
</bean>