使用Jackson无法正确序列化/反序列化ZonedDateTime问题
解决ZonedDateTime序列化反序列化时区不一致问题
你的测试用例中,ZonedDateTime序列化后反序列化时丢失了原有时区信息,被转换为UTC时区,导致断言失败。这是因为默认JavaTimeModule处理日期字符串时,未明确保留时区标识的解析规则,且默认会自动调整时区到UTC。
解决方案
关键配置要点
- 禁用Jackson自动调整时区的特性
- 为ZonedDateTime指定包含时区信息的序列化/反序列化格式
代码实现
方式一:自定义JavaTimeModule并配置格式化器
// 创建JavaTime模块并配置ZonedDateTime的序列化反序列化规则 JavaTimeModule javaTimeModule = new JavaTimeModule(); DateTimeFormatter zonedFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX['['VV']']"); javaTimeModule.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer(zonedFormatter)); javaTimeModule.addDeserializer(ZonedDateTime.class, new ZonedDateTimeDeserializer(zonedFormatter)); ObjectMapper mapper = new ObjectMapper() .enable(MapperFeature.DEFAULT_VIEW_INCLUSION) .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .registerModule(javaTimeModule) // 核心:禁止自动将日期转换为ObjectMapper默认时区 .configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false); // 测试代码 ZonedDateTime dateTime = ZonedDateTime.now(); String json = mapper.writeValueAsString(dateTime); LOGGER.info("ZonedDateTime json: " + json); ZonedDateTime dateTime2 = mapper.readValue(json, ZonedDateTime.class); assertEquals(dateTime, dateTime2);
方式二:通过Mixin简化配置
如果不想直接修改模块的序列化器,可以用Mixin类指定规则:
// 定义Mixin类 abstract class ZonedDateTimeMixin { @JsonSerialize(using = ZonedDateTimeSerializer.class) @JsonDeserialize(using = ZonedDateTimeDeserializer.class) private ZonedDateTime zonedDateTime; public ZonedDateTimeMixin( @JsonProperty("value") String value, @JsonProperty("offset") ZoneOffset offset, @JsonProperty("zone") ZoneId zone) {} } // 配置ObjectMapper ObjectMapper mapper = new ObjectMapper() .enable(MapperFeature.DEFAULT_VIEW_INCLUSION) .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .registerModule(new JavaTimeModule()) .configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false) .addMixIn(ZonedDateTime.class, ZonedDateTimeMixin.class); // 测试代码同上
原问题原因
你原代码中调用了findAndRegisterModules(),虽然能注册JavaTimeModule,但默认的ZonedDateTime反序列化逻辑会将带时区的字符串先解析为Instant,再转换为UTC时区的ZonedDateTime,同时Jackson默认开启ADJUST_DATES_TO_CONTEXT_TIME_ZONE,会自动把日期调整为ObjectMapper的默认时区(UTC),最终导致原有时区丢失。
内容的提问来源于stack exchange,提问作者zjffdu
相关产品推荐
相关产品推荐

