You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的自动调整行为:

  1. 编写自定义适配器类:
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());
    }
}
  1. 在实体类对应的日期字段上添加适配器注解:
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,配合自定义适配器实现宽松解析:

  1. 编写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);
    }
}
  1. 修改实体类字段:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 17:05:19