Jackson解析结构化LocalDateTime字段报错的解决方案咨询
你当前使用的自定义LocalDateTimeDeserializer默认将输入内容按字符串解析,但入参中dateTimeMessageSend是拆分字段的结构化JSON对象,解析器遇到START_OBJECT节点时无法直接转换为字符串,因此抛出解析异常。同时该自定义反序列化器覆盖了Jackson JavaTimeModule自带的、可直接解析这类结构化时间对象的默认逻辑。
以下是两种不修改入参结构的可行解决方案,按需选择即可:
方案1:复用Jackson内置反序列化逻辑(最快)
如果当前场景不需要兼容ISO字符串格式的LocalDateTime入参,直接移除自定义反序列化器、修正ObjectMapper配置即可:
- 删除你自定义的
LocalDateTimeDeserializer类,不再向JavaTimeModule手动注册自定义LocalDateTime反序列化器。 - 修正ObjectMapper配置的模块重复注册问题:不要同时手动注册JavaTimeModule和调用
findAndRegisterModules(),避免后注册的模块覆盖自定义配置,二选一即可:- 手动配置模式:保留JavaTimeModule、Jdk8Module的手动注册逻辑,移除
this.objectMapper.findAndRegisterModules();,示例代码:
JavaTimeModule javaTimeModule = new JavaTimeModule(); // 移除自定义反序列化器注册行:javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer()); this.objectMapper.registerModule(javaTimeModule); this.objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); this.objectMapper.registerModule(new Jdk8Module()); // 移除自动扫描模块的调用- 自动扫描模式:移除手动注册JavaTimeModule的逻辑,直接调用自动扫描方法,同时关闭时间戳序列化特性,示例代码:
this.objectMapper.findAndRegisterModules(); this.objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - 手动配置模式:保留JavaTimeModule、Jdk8Module的手动注册逻辑,移除
注意:该方案仅支持解析当前入参的结构化对象格式,若需同时兼容ISO格式字符串(如"2021-04-19T14:52:44.183251")作为LocalDateTime入参,需使用方案2。
方案2:重写自定义反序列化器,兼容多格式入参
如果业务需要同时支持「ISO字符串格式」和「拆分字段结构化对象格式」的LocalDateTime入参,重写LocalDateTimeDeserializer的解析逻辑,先判断当前JSON节点类型,再分别处理:
public class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> { private static final long serialVersionUID = 1L; protected LocalDateTimeDeserializer() { super(LocalDateTime.class); } @Override public LocalDateTime deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException { // 判断当前JSON节点类型 if (jp.currentToken() == JsonToken.VALUE_STRING) { // 处理字符串格式入参 return LocalDateTime.parse(jp.getText()); } else if (jp.currentToken() == JsonToken.START_OBJECT) { // 处理结构化对象入参,读取时间字段组装对象 JsonNode node = jp.getCodec().readTree(jp); int year = node.get("year").asInt(); int monthValue = node.get("monthValue").asInt(); int dayOfMonth = node.get("dayOfMonth").asInt(); int hour = node.get("hour").asInt(); int minute = node.get("minute").asInt(); int second = node.get("second").asInt(); int nano = node.get("nano").asInt(); // chronology字段为Jackson序列化时附带的元数据,无需读取 return LocalDateTime.of(year, monthValue, dayOfMonth, hour, minute, second, nano); } // 不支持的类型抛出标准异常 throw ctxt.mappingException(LocalDateTime.class, "不支持的LocalDateTime入参格式"); } }
配置ObjectMapper时同样需要注意避免自定义反序列化器被覆盖:要么移除findAndRegisterModules()调用,要么确保自定义JavaTimeModule在findAndRegisterModules之后注册。
额外注意:你当前定义的
ServiceData是ContentModel的非静态内部类,Jackson无法直接实例化非静态内部类(依赖外部类实例),需要给ServiceData类添加static修饰,改为静态内部类,否则会触发其他反序列化异常。
内容的提问来源于stack exchange,提问作者Jelly

