SpringBoot中XMLGregorianCalendar时间格式转换适配失效问题
XMLGregorianCalendar适配器未生效的时间格式转换问题
我需要处理两种时间格式的转换场景:
- 将外部API返回的
06-09-2022 17:05:28:113319格式字符串,转换为XMLGregorianCalendar实例 - 将
XMLGregorianCalendar转换为2022-09-06T17:07:21.319+02:00格式,用于调用另一API
由于项目大量使用WSDL定义的数据结构,我基于Java SpringBoot实现了DateAdapter,但该适配器并未生效,代码如下:
public class DateAdapter extends XmlAdapter<String, XMLGregorianCalendar> { private static final String DATE_STRING = "dd-MM-yyyy HH:mm:ss:SSSSSSSX"; @Override public String marshal(XMLGregorianCalendar v) throws Exception { SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_STRING); return dateFormat.format(v.toGregorianCalendar().getTime()); } @Override public XMLGregorianCalendar unmarshal(String v) throws Exception { GregorianCalendar gregorianCalendar = new GregorianCalendar(); SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_STRING); gregorianCalendar.setTime(dateFormat.parse(v)); return DatatypeFactory.newInstance().newXMLGregorianCalendar(gregorianCalendar); } }
问题排查与修复方案
1. 未绑定适配器到目标字段/类
JAXB不会自动识别自定义适配器,必须显式声明:
- 单个字段生效:在对应的字段或其getter方法上添加注解:
@XmlJavaTypeAdapter(DateAdapter.class) private XMLGregorianCalendar targetDateField;
- 全局生效:在项目的
package-info.java文件中配置全局适配器:
@XmlJavaTypeAdapters({ @XmlJavaTypeAdapter(type = XMLGregorianCalendar.class, value = DateAdapter.class) }) package com.your.project.package; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters; import javax.xml.datatype.XMLGregorianCalendar;
2. 时间格式字符串错误
原代码中的格式字符串存在两处问题:
SSSSSS:SimpleDateFormat的S最多支持3位毫秒,6位微秒会被错误解析X:输入字符串无时区信息,使用X会导致解析失败
修正输入解析逻辑
针对06-09-2022 17:05:28:113319格式,手动截取前3位毫秒处理:
@Override public XMLGregorianCalendar unmarshal(String v) throws Exception { // 拆分日期和微秒部分 String[] timeParts = v.split(":"); String dateTimePart = String.join(":", Arrays.copyOf(timeParts, timeParts.length - 1)); String microsecPart = timeParts[timeParts.length - 1]; // 解析基础日期时间,加上前3位毫秒 SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); Date baseDate = sdf.parse(dateTimePart); long millis = Long.parseLong(microsecPart.substring(0, 3)); baseDate.setTime(baseDate.getTime() + millis); GregorianCalendar gregCal = new GregorianCalendar(); gregCal.setTime(baseDate); // 指定时区(根据实际业务设置,比如+02:00对应Europe/Paris) gregCal.setTimeZone(TimeZone.getTimeZone("Europe/Paris")); return DatatypeFactory.newInstance().newXMLGregorianCalendar(gregCal); }
简化输出格式化逻辑
XMLGregorianCalendar本身支持直接输出符合要求的格式,无需使用SimpleDateFormat:
@Override public String marshal(XMLGregorianCalendar v) throws Exception { // toXMLFormat()直接返回如2022-09-06T17:07:21.319+02:00的格式 return v.toXMLFormat(); }
3. 线程安全问题
SimpleDateFormat是非线程安全的,高并发场景下会出现异常,建议替换为Java 8+的DateTimeFormatter:
public class DateAdapter extends XmlAdapter<String, XMLGregorianCalendar> { private static final DateTimeFormatter INPUT_FORMATTER = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss:SSS"); private static final ZoneId TIME_ZONE = ZoneId.of("Europe/Paris"); @Override public String marshal(XMLGregorianCalendar v) throws Exception { return v.toXMLFormat(); } @Override public XMLGregorianCalendar unmarshal(String v) throws Exception { // 截取到3位毫秒 String truncatedTime = v.substring(0, v.length() - 3); LocalDateTime localDateTime = LocalDateTime.parse(truncatedTime, INPUT_FORMATTER); ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, TIME_ZONE); GregorianCalendar gregCal = GregorianCalendar.from(zonedDateTime); return DatatypeFactory.newInstance().newXMLGregorianCalendar(gregCal); } }
内容的提问来源于stack exchange,提问作者DerZander
相关产品推荐
相关产品推荐

