javax.xml.bind.JAXBContext操作XML的实现示例
作者:隔壁阿布都
JAXB(Java Architecture for XML Binding)是 Java 标准 API(JDK 1.6+ 内置,JDK 9+ 需手动引入依赖),用于实现 Java 对象与 XML 文档的相互转换(序列化/反序列化)。核心类 JAXBContext 负责管理 XML 与 Java 类型的绑定关系,配合 Marshaller(对象转 XML)和 Unmarshaller(XML 转对象)完成核心操作。
一、前置准备(依赖说明)
1. JDK 版本差异
- JDK 8 及以下:JAXB 是 JDK 内置模块(
javax.xml.bind),无需额外依赖。 - JDK 9 及以上:JAXB 被移除出默认模块(模块化改革),需手动引入 Maven/Gradle 依赖(迁移到 Jakarta EE 后包名不变,但需指定依赖):
Maven 依赖(JDK 9+)
<!-- JAXB 核心依赖 -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- 运行时实现(必须,否则报错 ClassNotFoundException) -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.3</version>
<scope>runtime</scope>
</dependency>
<!-- 用于 JDK 9+ 模块支持(可选,解决模块访问限制) -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.3</version>
</dependency>
Gradle 依赖(JDK 9+)
implementation 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.3' runtimeOnly 'com.sun.xml.bind:jaxb-impl:2.3.3' runtimeOnly 'com.sun.xml.bind:jaxb-core:2.3.3'
二、核心注解说明(Java 实体 → XML 映射)
JAXB 通过注解定义 Java 类与 XML 元素的映射规则,常用注解如下:
| 注解 | 作用场景 | 说明 |
|---|---|---|
| @XmlRootElement | 类级别 | 标记该类为 XML 根元素(必须,否则无法序列化),可指定 name(XML 根节点名) |
| @XmlElement | 字段/ getter 方法 | 映射 Java 字段到 XML 子元素,可指定 name(XML 子节点名)、required(是否必选) |
| @XmlAttribute | 字段/ getter 方法 | 映射 Java 字段到 XML 元素的属性(而非子元素) |
| @XmlTransient | 字段/ getter 方法 | 忽略该字段(不参与 XML 序列化/反序列化) |
| @XmlAccessorType | 类级别 | 指定字段访问策略(如 FIELD:直接访问字段,无需 getter/setter) |
| @XmlList | 集合字段 | 将集合元素序列化为 XML 单个元素的多个值(用空格分隔) |
| @XmlRootElement | 类级别 | 根元素注解,必须存在 |
三、完整使用步骤(示例驱动)
场景:实现User类与 XML 的相互转换,包含嵌套对象、集合、属性
步骤 1:定义 Java 实体类(带 JAXB 注解)
import javax.xml.bind.annotation.*;
import java.util.List;
// 根元素:XML 根节点名为 <user>
@XmlRootElement(name = "user")
// 访问策略:直接操作字段(无需 getter/setter,简化代码)
@XmlAccessorType(XmlAccessType.FIELD)
public class User {
// XML 元素的属性:<user id="1001">
@XmlAttribute(name = "id")
private Long userId;
// XML 子元素:<username>张三</username>(必选)
@XmlElement(name = "username", required = true)
private String name;
// XML 子元素:<age>25</age>
@XmlElement
private Integer age;
// 嵌套对象(XML 子元素嵌套)
@XmlElement(name = "address")
private Address address;
// 集合字段(XML 子元素:<hobbies>篮球 足球</hobbies>)
@XmlElement(name = "hobbies")
@XmlList // 集合元素序列化为单个元素的多个值
private List<String> hobbies;
// 忽略该字段(不生成 XML 元素)
@XmlTransient
private String password;
// 嵌套类(地址信息)
@XmlAccessorType(XmlAccessType.FIELD)
public static class Address {
@XmlElement(name = "province")
private String province;
@XmlElement(name = "city")
private String city;
// 必须有默认无参构造器(JAXB 反射创建对象时需要)
public Address() {}
public Address(String province, String city) {
this.province = province;
this.city = city;
}
// getter/setter(可选,因使用 FIELD 访问策略)
// ...
}
// 必须有默认无参构造器(JAXB 强制要求)
public User() {}
// 带参构造器(方便创建对象)
public User(Long userId, String name, Integer age, Address address, List<String> hobbies) {
this.userId = userId;
this.name = name;
this.age = age;
this.address = address;
this.hobbies = hobbies;
}
// getter/setter(可选,若需外部操作字段)
// ...
}
关键注意:
- 所有参与序列化的类(包括嵌套类)必须有 默认无参构造器(JAXB 反射实例化需要)。
- 注解可加在字段或 getter 方法上,建议统一风格(如示例用
XmlAccessType.FIELD直接操作字段)。
步骤 2:Java 对象 → XML(序列化,Marshaller)
核心流程:
- 通过 JAXBContext.newInstance(Class...) 创建上下文(指定要绑定的 Java 类)。
- 从上下文获取 Marshaller 实例。
- 配置 Marshaller(如格式化输出、编码、忽略XML声明等)。
- 调用 marshall() 方法序列化(输出到文件、流、字符串等)。
示例代码:对象转 XML 文件/字符串
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.File;
import java.io.StringWriter;
import java.util.Arrays;
public class JaxbMarshallerDemo {
public static void main(String[] args) {
// 1. 创建 Java 对象(模拟数据)
User.Address address = new User.Address("广东省", "深圳市");
User user = new User(
1001L,
"张三",
25,
address,
Arrays.asList("篮球", "足球", "编程")
);
try {
// 2. 创建 JAXBContext(参数为要绑定的实体类)
JAXBContext context = JAXBContext.newInstance(User.class);
// 3. 获取 Marshaller 实例(负责序列化)
Marshaller marshaller = context.createMarshaller();
// 4. 配置 Marshaller
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // 格式化输出(换行、缩进)
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); // 编码格式
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false); // 是否省略 XML 声明(<?xml ...?>)
// 5. 序列化到 XML 文件
File xmlFile = new File("user.xml");
marshaller.marshal(user, xmlFile);
System.out.println("XML 文件已生成:" + xmlFile.getAbsolutePath());
// 6. 序列化到字符串(便于网络传输等场景)
StringWriter writer = new StringWriter();
marshaller.marshal(user, writer);
String xmlStr = writer.toString();
System.out.println("\nXML 字符串:");
System.out.println(xmlStr);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
生成的 XML 文件(user.xml)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user id="1001">
<username>张三</username>
<age>25</age>
<address>
<province>广东省</province>
<city>深圳市</city>
</address>
<hobbies>篮球 足球 编程</hobbies>
</user>
步骤 3:XML → Java 对象(反序列化,Unmarshaller)
核心流程:
- 同样通过
JAXBContext.newInstance(Class...)创建上下文。 - 从上下文获取
Unmarshaller实例。 - 调用
unmarshal()方法反序列化(从文件、流、字符串读取 XML)。
示例代码:XML 文件/字符串转对象
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.io.StringReader;
public class JaxbUnmarshallerDemo {
public static void main(String[] args) {
try {
// 1. 创建 JAXBContext
JAXBContext context = JAXBContext.newInstance(User.class);
// 2. 获取 Unmarshaller 实例(负责反序列化)
Unmarshaller unmarshaller = context.createUnmarshaller();
// 3. 从 XML 文件反序列化到对象
File xmlFile = new File("user.xml");
User userFromFile = (User) unmarshaller.unmarshal(xmlFile);
System.out.println("从文件解析的 User:");
System.out.println("用户ID:" + userFromFile.getUserId());
System.out.println("姓名:" + userFromFile.getName());
System.out.println("地址:" + userFromFile.getAddress().getProvince() + "-" + userFromFile.getAddress().getCity());
System.out.println("爱好:" + userFromFile.getHobbies());
// 4. 从 XML 字符串反序列化到对象
String xmlStr = """
<?xml version="1.0" encoding="UTF-8"?>
<user id="1002">
<username>李四</username>
<age>30</age>
<address>
<province>浙江省</province>
<city>杭州市</city>
</address>
<hobbies>读书 旅行</hobbies>
</user>
""";
StringReader reader = new StringReader(xmlStr);
User userFromStr = (User) unmarshaller.unmarshal(reader);
System.out.println("\n从字符串解析的 User:");
System.out.println("用户ID:" + userFromStr.getUserId());
System.out.println("姓名:" + userFromStr.getName());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
输出结果
从文件解析的 User:
用户ID:1001
姓名:张三
地址:广东省-深圳市
爱好:[篮球, 足球, 编程]从字符串解析的 User:
用户ID:1002
姓名:李四
地址:浙江省-杭州市
爱好:[读书, 旅行]
四、复杂场景扩展
1. 集合根节点(多个对象的 XML 序列化)
若需序列化多个 User 对象(如 List<User>),直接序列化集合会报错(无根元素),需创建一个“包装类”:
@XmlRootElement(name = "users")
@XmlAccessorType(XmlAccessType.FIELD)
public class UserList {
@XmlElement(name = "user") // 每个 User 对应 <user> 子元素
private List<User> userList;
public UserList() {}
public UserList(List<User> userList) {
this.userList = userList;
}
// getter/setter
}
序列化示例:
List<User> users = Arrays.asList(user1, user2);
UserList userList = new UserList(users);
marshaller.marshal(userList, new File("users.xml"));
生成的 XML:
<users>
<user id="1001">...</user>
<user id="1002">...</user>
</users>
2. 日期格式自定义
默认日期字段(Date/LocalDate)序列化格式不友好,可通过 @XmlJavaTypeAdapter 自定义适配器:
步骤 1:实现日期适配器
import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.text.SimpleDateFormat;
import java.util.Date;
// 适配 Date <-> String(自定义格式)
public class DateAdapter extends XmlAdapter<String, Date> {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public Date unmarshal(String xmlStr) throws Exception {
return sdf.parse(xmlStr);
}
@Override
public String marshal(Date date) throws Exception {
return sdf.format(date);
}
}
步骤 2:在实体类字段上使用
@XmlElement(name = "createTime") @XmlJavaTypeAdapter(DateAdapter.class) // 应用适配器 private Date createTime;
序列化后 XML 效果:
<createTime>2025-11-25 14:30:00</createTime>
3. 忽略空值字段
默认情况下,空值字段(如 age = null)会生成空的 XML 元素(<age/>),可通过配置 Marshaller 忽略空值:
// JAXB 2.2+ 支持,需引入 jaxb-impl 依赖
marshaller.setProperty("com.sun.xml.bind.marshaller.NullPolicy", NullPolicy.SKIP);
或通过注解 @XmlElement(nillable = false) 配合配置:
@XmlElement(nillable = false) private Integer age;
五、常见问题与注意事项
1. 报错JAXBException: Class ... nor any of its super class is known to this context
- 原因:
JAXBContext.newInstance(...)未传入所有参与序列化的类(如嵌套类、集合元素类)。 - 解决:确保所有相关类都被包含,如
JAXBContext.newInstance(User.class, Address.class)。
2. 报错No default constructor found
- 原因:实体类(或嵌套类)没有默认无参构造器。
- 解决:为所有实体类添加无参构造器(即使是空实现)。
3. 中文乱码
- 原因:
Marshaller编码未设置为 UTF-8,或文件读取时编码不匹配。 - 解决:
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); // 序列化时 // 反序列化文件时指定编码 FileInputStream fis = new FileInputStream(xmlFile); User user = (User) unmarshaller.unmarshal(new InputStreamReader(fis, "UTF-8"));
4. JDK 9+ 模块访问限制
- 报错:
Module java.base does not "opens java.lang" to unnamed module @xxx。 - 解决:运行时添加 JVM 参数,开放模块访问:
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED
六、核心 API 总结
| 类/接口 | 作用 | 核心方法 |
|---|---|---|
| JAXBContext | 管理绑定关系(线程安全,可复用) | newInstance(Class...):创建上下文 |
| Marshaller | Java 对象 → XML | marshal(Object, OutputStream/File/StringWriter) |
| Unmarshaller | XML → Java 对象 | unmarshal(InputStream/File/StringReader) |
通过以上步骤,你可以完整实现 XML 与 Java 对象的相互转换,覆盖基本场景和常见复杂需求。JAXB 无需手动解析 XML 标签,通过注解自动映射,大幅简化 XML 处理代码。
到此这篇关于javax.xml.bind.JAXBContext操作XML的实现示例的文章就介绍到这了,更多相关javax.xml.bind.JAXBContext操作XML内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
