SpringBoot多格式LocalDateTime兼容单值/集合/Map反序列化方案咨询
问题根本原因
- 原反序列化器实现存在Bug:直接将
JsonParser传递给新建的LocalDateTimeDeserializer解析,第一次解析失败后JsonParser的读取指针已偏移,后续格式尝试都会读取空值或错误内容,永远匹配不到第二个格式。 - 原
ObjectMapper配置使用modules()方法会覆盖Spring Boot默认注册的JavaTimeModule等内置模块,且未明确自定义反序列化器的优先级,导致默认的LocalDateTime反序列化器仍被优先调用。 - 原自定义注解仅配置了
@JsonDeserialize(using = xxx),仅对单个LocalDateTime属性生效,集合/Map的元素反序列化需要额外配置contentUsing属性。
可行解决方案
方案1:全局配置(无需加注解,全应用生效)
步骤1:修复自定义反序列化器
先读取完整的日期字符串再逐格式匹配,避免JsonParser指针偏移问题:
public class CustomDateTimeDeser extends JsonDeserializer<LocalDateTime> { private static final String[] DATE_FORMATS = {"MM/dd/yyyy HH:mm", "yyyy-MM-dd'T'HH:mm:ss"}; private static final ZoneId ZONE_ID = TimeZone.getTimeZone("America/New_York").toZoneId(); @Override public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { String dateStr = jsonParser.getValueAsString(); for (String format : DATE_FORMATS) { try { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format).withZone(ZONE_ID); return LocalDateTime.parse(dateStr, formatter); } catch (Exception ignored) { } } throw new JsonParseException(jsonParser, "Unable to parse date: [" + dateStr + "]. Supported formats: " + Arrays.toString(DATE_FORMATS)); } }
步骤2:正确配置ObjectMapper
使用modulesToInstall追加自定义模块,不会覆盖默认模块,确保自定义反序列化器优先生效:
@Bean @Primary public ObjectMapper objectMapper() { SimpleModule customModule = new SimpleModule(); customModule.addDeserializer(LocalDateTime.class, new CustomDateTimeDeser()); return Jackson2ObjectMapperBuilder.json() .modulesToInstall(customModule) .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .build(); }
该配置自动覆盖所有LocalDateTime相关的反序列化场景,包括单个属性、List、Set、Map值的反序列化,无需额外加注解。
方案2:自定义注解方案(仅标注的属性生效)
如果不需要全局生效,仅希望加注解的属性支持多格式,修改自定义注解同时配置using和contentUsing:
@JacksonAnnotationsInside @Retention(RetentionPolicy.RUNTIME) @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "America/New_York") // 同时配置单个类型反序列化和集合内容反序列化 @JsonDeserialize(using = CustomDateTimeDeser.class, contentUsing = CustomDateTimeDeser.class) public @interface CustomDateTimeFormat {}
使用时直接在对应属性上加@CustomDateTimeFormat即可,不管是单个LocalDateTime还是集合、Map类型的属性都能正常反序列化。
内容的提问来源于stack exchange,提问作者ASPEN3
相关产品推荐
相关产品推荐

