Java 11传入无效日期2020-11-31遇解组错误,Java 8无此问题
解决Java 11中无效XML日期解组错误的方案
在处理包含无效日期(如<accountLastUpdatedDate>2020-11-31T00:00:00</accountLastUpdatedDate>)的XML时,Java 8的JAXB会自动把这种无效日期调整为下一个有效日期(比如2020-12-01),但Java 11里会直接抛出解组错误,返回SOAP Fault提示日期格式无效。以下是几种可行的解决办法:
方法一:自定义XMLGregorianCalendar适配器
JAXB支持通过XmlAdapter自定义类型转换逻辑,我们可以写一个适配器来模拟Java 8的自动调整行为:
- 编写自定义适配器类:
import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.GregorianCalendar; public class LenientXMLGregorianCalendarAdapter extends XmlAdapter<String, XMLGregorianCalendar> { private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); @Override public XMLGregorianCalendar unmarshal(String dateStr) throws Exception { DATE_FORMAT.setLenient(true); // 开启宽松模式,允许自动调整无效日期 Calendar calendar = Calendar.getInstance(); try { calendar.setTime(DATE_FORMAT.parse(dateStr)); } catch (ParseException e) { throw new IllegalArgumentException("日期格式无效", e); } GregorianCalendar gregorianCalendar = new GregorianCalendar(); gregorianCalendar.setTime(calendar.getTime()); return DatatypeFactory.newInstance().newXMLGregorianCalendar(gregorianCalendar); } @Override public String marshal(XMLGregorianCalendar xmlGregorianCalendar) throws Exception { return DATE_FORMAT.format(xmlGregorianCalendar.toGregorianCalendar().getTime()); } }
- 在实体类对应的日期字段上添加适配器注解:
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import javax.xml.datatype.XMLGregorianCalendar; public class Account { @XmlElement(name = "accountLastUpdatedDate") @XmlJavaTypeAdapter(LenientXMLGregorianCalendarAdapter.class) private XMLGregorianCalendar accountLastUpdatedDate; // 此处添加getter和setter方法 }
方法二:开启JAXB日期解析宽松模式
如果用的是Sun/Oracle的JAXB实现(比如jaxb-impl),可以通过系统参数直接开启宽松解析,无需修改代码:
- 启动应用时添加参数:
-Dcom.sun.xml.bind.datetime.lenient=true
- 或者在代码初始化阶段设置:
System.setProperty("com.sun.xml.bind.datetime.lenient", "true");
注意:这个参数只对特定JAXB实现生效,如果用的是EclipseLink MOXy这类其他实现,需要查对应配置。
方法三:改用Java 8+日期API
如果允许修改实体类字段类型,建议换成Java 8引入的java.time包下的LocalDateTime,配合自定义适配器实现宽松解析:
- 编写LocalDateTime适配器:
import javax.xml.bind.annotation.adapters.XmlAdapter; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; public class LenientLocalDateTimeAdapter extends XmlAdapter<String, LocalDateTime> { private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() .appendPattern("yyyy-MM-dd'T'HH:mm:ss") .parseDefaulting(ChronoField.NANO_OF_SECOND, 0) .toFormatter() .withResolverStyle(java.time.format.ResolverStyle.LENIENT); // 启用宽松解析 @Override public LocalDateTime unmarshal(String dateStr) throws Exception { return LocalDateTime.parse(dateStr, FORMATTER); } @Override public String marshal(LocalDateTime localDateTime) throws Exception { return localDateTime.format(FORMATTER); } }
- 修改实体类字段:
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.time.LocalDateTime; public class Account { @XmlElement(name = "accountLastUpdatedDate") @XmlJavaTypeAdapter(LenientLocalDateTimeAdapter.class) private LocalDateTime accountLastUpdatedDate; // 此处添加getter和setter方法 }
内容的提问来源于stack exchange,提问作者Megha Rathi
相关产品推荐
相关产品推荐

