Java 11中ZonedDateTime毫秒部分末尾0被截断问题求助
问题解决方案:Spring Boot 2.7.8 + Java 11 下 ZonedDateTime 作为 Map 键序列化时毫秒末尾 0 被截断
问题原因
Spring Boot 默认使用 Jackson 处理 JSON 序列化/反序列化,Jackson 对 ZonedDateTime 的默认序列化逻辑会根据时间的实际精度自动省略末尾的零毫秒(比如将 630 简化为 63)。当 ZonedDateTime 作为 Map 的键时,这种自动简化会导致响应格式与请求格式不一致。
解决方案
通过自定义 Jackson 的键序列化器和反序列化器,强制指定固定的日期时间格式,确保毫秒部分始终保留三位数字。
1. 自定义 ZonedDateTime 键序列化器
创建序列化器类,将 ZonedDateTime 格式化为包含三位毫秒的字符串:
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdKeySerializer; import java.io.IOException; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; public class ZonedDateTimeKeySerializer extends StdKeySerializer { // 匹配请求中的格式:保留三位毫秒,带时区标识 private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX['['VV']']"); @Override public void serialize(Object key, JsonGenerator gen, SerializerProvider provider) throws IOException { if (key instanceof ZonedDateTime) { gen.writeFieldName(FORMATTER.format((ZonedDateTime) key)); } else { super.serialize(key, gen, provider); } } }
2. 自定义 ZonedDateTime 键反序列化器
确保请求中的日期字符串能被正确解析为带三位毫秒的 ZonedDateTime 对象:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializer; import java.io.IOException; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; public class ZonedDateTimeKeyDeserializer extends StdKeyDeserializer { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX['['VV']']"); protected ZonedDateTimeKeyDeserializer() { super(ZonedDateTime.class); } @Override protected Object _parse(String key, DeserializationContext ctxt) throws IOException { return ZonedDateTime.parse(key, FORMATTER); } }
3. 注册自定义序列化/反序列化器到 Spring
创建 Jackson 配置类,将自定义组件注册到 ObjectMapper:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.time.ZonedDateTime; @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); // 注册键序列化器 module.addKeySerializer(ZonedDateTime.class, new ZonedDateTimeKeySerializer()); // 注册键反序列化器 module.addKeyDeserializer(ZonedDateTime.class, new ZonedDateTimeKeyDeserializer()); objectMapper.registerModule(module); return objectMapper; } }
验证效果
配置完成后,Map 的键会严格按照 yyyy-MM-dd'T'HH:mm:ss.SSSXXX['['VV']'] 格式序列化,响应中的毫秒部分将保留三位数字,与请求格式完全一致:
{ "dateList": { "2023-06-23T18:13:06.630Z[UTC]": "2023-06-23T11:13:06.630-07:00" } }
内容的提问来源于stack exchange,提问作者Ranjit Meher
相关产品推荐
相关产品推荐

