Jackson反序列化JSON日期字符串至Java Date时遇格式错误求助
嘿Farhan,我来帮你分析下这个问题~
从你给出的错误栈能看出关键问题:你的Jackson自定义反序列化器CustomDateDeserializer根本没被执行,实际在处理日期解析的是JodaTime的DateTimeFormatter和JAXB的DateAdapter,还有EclipseLink MOXy框架——这说明你的项目里的JSON处理逻辑被其他框架接管了,Jackson的注解和自定义类没生效。
问题原因分析
错误日志里的这些信息是突破口:
Caused by: java.lang.IllegalArgumentException: Invalid format: "2017-12-27" is too short
at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:945) ~[joda-time-2.9.1.jar:2.9.1]
at com.exxonmobil.ace.hybris.jaxb.DateAdapter.unmarshal(DateAdapter.java:45) ~[DateAdapter.class:?]
at org.eclipse.persistence.internal.jaxb.XMLJavaTypeConverter.convertDataValueToObjectValue(XMLJavaTypeConverter.java:149) ~[org.eclipse.persistence.moxy-2.6.1.jar:?]
org.eclipse.persistence.moxy是EclipseLink的MOXy框架,它是JAXB的一个实现,既可以处理XML也可以处理JSONDateAdapter是你项目里的JAXB适配器类,它在用JodaTime的格式化器解析日期,但它预期的日期格式比yyyy-MM-dd更长,所以报错“太短”
这意味着你的请求数据没有经过Jackson处理,而是被MOXy/JAXB的流程接管了,所以你写的Jackson自定义反序列化器完全没起作用。
解决方案
方案1:切换回Jackson作为默认JSON处理器
如果你的项目本来应该用Jackson处理JSON,那需要排除MOXy的依赖,确保Jackson的优先级更高:
- 如果你用Maven,可以在相关依赖里排除MOXy:
<dependency> <!-- 这里是引入MOXy的那个依赖,比如spring-boot-starter-web-services之类的 --> <groupId>xxx</groupId> <artifactId>xxx</artifactId> <exclusions> <exclusion> <groupId>org.eclipse.persistence</groupId> <artifactId>org.eclipse.persistence.moxy</artifactId> </exclusion> </exclusions> </dependency> - 若是Spring Boot项目,可以在
application.properties里明确指定JSON处理器:spring.mvc.converters.preferred-json-mapper=jackson
方案2:修改JAXB的DateAdapter支持短日期格式
如果你的项目必须使用MOXy/JAXB处理,那需要修改项目里的DateAdapter类,让它支持yyyy-MM-dd格式:
import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormat; import javax.xml.bind.annotation.adapters.XmlAdapter; import java.util.Date; public class DateAdapter extends XmlAdapter<String, Date> { // 支持你需要的yyyy-MM-dd格式 private static final DateTimeFormatter SHORT_DATE_FORMAT = DateTimeFormat.forPattern("yyyy-MM-dd") .withZone(DateTimeZone.UTC); // 如果原来还有长格式的需求,可以保留并做兼容 private static final DateTimeFormatter ORIGINAL_FORMAT = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ") .withZone(DateTimeZone.UTC); @Override public Date unmarshal(String dateStr) throws Exception { if (dateStr == null) { return null; } // 先尝试短格式,失败再尝试原格式 try { return new Date(SHORT_DATE_FORMAT.parseMillis(dateStr)); } catch (IllegalArgumentException e) { try { return new Date(ORIGINAL_FORMAT.parseMillis(dateStr)); } catch (IllegalArgumentException ex) { throw new RuntimeException("无法解析日期字符串: " + dateStr, ex); } } } @Override public String marshal(Date date) throws Exception { if (date == null) { return null; } // 序列化可以按需求选择格式,这里用短格式举例 return SHORT_DATE_FORMAT.print(date.getTime()); } }
方案3:确保Jackson配置生效
如果确认要使用Jackson,除了注解外,也可以全局注册自定义反序列化器,避免注解失效:
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; // 在配置类里初始化ObjectMapper @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); // 全局注册自定义日期反序列化器 module.addDeserializer(Date.class, new CustomDateDeserializer()); mapper.registerModule(module); return mapper; } }
总结
先重点排查项目的JSON处理器到底是Jackson还是MOXy/JAXB,从错误栈来看后者在起作用,所以要么切换回Jackson,要么修改JAXB的适配器来支持目标日期格式。
内容的提问来源于stack exchange,提问作者Farhan

