@xmljavatypeadapter 是 jaxb 处理日期格式最常用且推荐的方式,用于可控双向转换 java.util.date、localdatetime 等类型与 xml 字符串,解决默认序列化兼容性问题。

Java JAXB 处理日期格式时,@XmlJavaTypeAdapter 是最常用且推荐的方式——它能将 java.util.Date、LocalDateTime、LocalDate 等类型与 XML 字符串之间做可控的双向转换,避免默认序列化(如毫秒时间戳或平台相关格式)带来的兼容性问题。
为什么需要 @XmlJavaTypeAdapter
JAXB 默认对 Date 类型使用 javax.xml.bind.DatatypeConverter,输出类似 2024-05-20T14:30:00.123+08:00 的 ISO 8601 格式;而对 LocalDateTime 等 Java 8 时间类则直接报错(不支持)。用 @XmlJavaTypeAdapter 可以:
- 统一指定输出格式(如
yyyy-MM-dd HH:mm:ss) - 适配不同时间类型(
LocalDateTime/LocalDate/ZonedDateTime) - 处理时区、空值、解析异常等边界情况
适配 LocalDateTime(推荐 Java 8+)
写一个自定义 Adapter:
public class LocalDateTimeAdapter extends XmlAdapter<String, LocalDateTime> {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public LocalDateTime unmarshal(String s) throws Exception {
if (s == null || s.trim().isEmpty()) return null;
return LocalDateTime.parse(s.trim(), FORMATTER);
}
@Override
public String marshal(LocalDateTime dateTime) throws Exception {
return (dateTime == null) ? null : dateTime.format(FORMATTER);
}
}
在字段上使用:
立即学习“Java免费学习笔记(深入)”;
public class Order {
@XmlElement
@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
private LocalDateTime createTime;
// getter/setter...
}
适配 Date(兼容老项目)
若仍用 java.util.Date,Adapter 可这样写:
public class DateAdapter extends XmlAdapter<String, Date> {
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
static { FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+8")); }
@Override
public Date unmarshal(String s) throws Exception {
if (s == null || s.trim().isEmpty()) return null;
return FORMAT.parse(s.trim());
}
@Override
public String marshal(Date date) throws Exception {
return (date == null) ? null : FORMAT.format(date);
}
}
注意:SimpleDateFormat 非线程安全,建议每个方法内新建实例,或用 ThreadLocal 封装(生产环境更稳妥)。
全局注册(避免重复标注)
如果多个类都用相同格式,可在 JAXBContext 创建时全局绑定:
Map<String, Object> props = new HashMap<>();
props.put("com.sun.xml.bind.defaultNamespacePrefix", "ns");
JAXBContext ctx = JAXBContext.newInstance(Order.class);
// 注册全局 adapter(需配合 @XmlJavaTypeAdapters)
// 或改用 Jakarta EE 方式:通过 package-info.java 声明
更简洁的方式是在包级声明 package-info.java:
@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(value = LocalDateTimeAdapter.class, type = LocalDateTime.class),
@XmlJavaTypeAdapter(value = DateAdapter.class, type = Date.class)
})
package com.example.model;
这样该包下所有字段自动生效,无需每个字段加注解。
基本上就这些。关键点是:选对时间类型、写好 Adapter 的 marshal/unmarshal 逻辑、注意线程安全和空值处理。不复杂但容易忽略细节。










