XML 序列化是指将 Java 对象转换为 XML 格式的字符串或文件的过程,反序列化则相反,是将 XML 格式的数据重新转换回 Java 对象的过程。

在 Java 中,主要有两种方式来实现 XML 序列化/反序列化:
- JAXB (Java Architecture for XML Binding):这是 Java 标准库(从 Java 6 开始内置)提供的方式,是现在推荐的做法,它通过注解将 Java 类和 XML 元素/属性绑定起来,非常方便。
- 传统方式(使用
javax.xml包):这种方式非常繁琐,需要手动创建Document对象、遍历节点等,代码量巨大,现在基本不再使用。
我们将重点介绍 JAXB,因为它更现代、更高效。
使用 JAXB (推荐方法)
JAXB 的核心思想是通过注解来定义 Java 对象与 XML 之间的映射关系。
核心注解
@XmlRootElement: 将一个类映射到 XML 的根元素。name属性指定 XML 元素的名称。@XmlElement: 将一个字段或属性映射到 XML 的一个元素。name属性指定 XML 元素的名称。@XmlAttribute: 将一个字段或属性映射到 XML 元素的属性。@XmlAccessorType: 控制哪些字段/属性会被序列化,常用值XmlAccessType.FIELD表示序列化所有非static和非transient的字段。@XmlElementWrapper: 用于包装一个列表类型的字段,使其在 XML 中表现为一个父元素,列表中的每个元素作为其子元素。
核心类
javax.xml.bind.JAXBContext: 上下文对象,是 JAXB API 的入口点,用于管理 XML/Java 绑定信息。javax.xml.bind.Marshaller: 负责将 Java 对象序列化为 XML。javax.xml.bind.Unmarshaller: 负责将 XML 反序列化为 Java 对象。
完整示例:使用 JAXB 序列化和反序列化
假设我们有一个 User 类,我们想将它转换为 XML,然后再从 XML 恢复成 User 对象。

步骤 1: 创建 Java 类并添加 JAXB 注解
我们定义 User 类,为了简化,我们使用 Lombok 来自动生成 getter, setter, toString 等方法,但即使不使用 Lombok,手动添加这些方法也可以。
User.java
import javax.xml.bind.annotation.*;
import java.util.List;
// 1. 指定这个类是 XML 的根元素,名称为 "user"
@XmlRootElement(name = "user")
// 2. 指定访问级别为 FIELD,即直接序列化/反序列化字段
@XmlAccessorType(XmlAccessType.FIELD)
public class User {
// 3. 映射为 XML 元素,名称为 "id"
@XmlElement(name = "id")
private int id;
// 4. 映射为 XML 元素,名称为 "name"
@XmlElement(name = "name")
private String name;
// 5. 使用 @XmlElementWrapper 包装列表,列表元素名为 "role"
@XmlElementWrapper(name = "roles")
@XmlElement(name = "role")
private List<String> roles;
// 默认构造函数是 JAXB 反序列化所必需的
public User() {
}
// 带参数的构造函数
public User(int id, String name, List<String> roles) {
this.id = id;
this.name = name;
this.roles = roles;
}
// Getter 和 Setter 方法 (Lombok 会自动生成)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", roles=" + roles +
'}';
}
}
步骤 2: 创建工具类进行序列化和反序列化
为了方便使用,我们创建一个 JAXBUtils 工具类。
JAXBUtils.java
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
public class JAXBUtils {
/**
* 将 Java 对象序列化为 XML 字符串
* @param obj 要序列化的对象
* @param clazz 对象的 Class 对象
* @return XML 字符串
*/
public static <T> void toFile(T obj, String filePath) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
// 格式化输出,使 XML 更易读
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// 设置编码
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
// 输出到文件
marshaller.marshal(obj, new java.io.File(filePath));
}
/**
* 将 Java 对象序列化为 XML 字符串
* @param obj 要序列化的对象
* @param clazz 对象的 Class 对象
* @return XML 字符串
*/
public static <T> String toXml(T obj) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
// 格式化输出,使 XML 更易读
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// 设置编码
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
Writer writer = new StringWriter();
// 将对象序列化到 writer 中
marshaller.marshal(obj, writer);
return writer.toString();
}
/**
* 将 XML 字符串反序列化为 Java 对象
* @param xml XML 字符串
* @param clazz 目标对象的 Class 对象
* @return 反序列化后的 Java 对象
*/
public static <T> T fromXml(String xml, Class<T> clazz) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = context.createUnmarshaller();
// 从 StringReader 中读取 XML 并进行反序列化
return (T) unmarshaller.unmarshal(new StringReader(xml));
}
}
步骤 3: 编写主程序进行测试
Main.java
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
// 1. 创建一个 User 对象
User user = new User(1, "张三", Arrays.asList("管理员", "开发者"));
try {
// 2. 序列化:将 User 对象转换为 XML 字符串
String xmlString = JAXBUtils.toXml(user);
System.out.println("序列化后的 XML:");
System.out.println(xmlString);
// 3. 反序列化:将 XML 字符串转换回 User 对象
User deserializedUser = JAXBUtils.fromXml(xmlString, User.class);
System.out.println("\n反序列化后的 User 对象:");
System.out.println(deserializedUser);
// 4. 序列化到文件
JAXBUtils.toFile(user, "user.xml");
System.out.println("\nXML 文件 'user.xml' 已生成。");
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果
执行 Main.java,控制台输出如下:
序列化后的 XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user>
<id>1</id>
<name>张三</name>
<roles>
<role>管理员</role>
<role>开发者</role>
</roles>
</user>
反序列化后的 User 对象:
User{id=1, name='张三', roles=[管理员, 开发者]}
XML 文件 'user.xml' 已生成。
项目根目录下会生成一个 user.xml 文件,内容与控制台打印的 XML 字符串一致。
处理复杂 XML 结构
对于更复杂的 XML,例如包含命名空间,JAXB 也能很好地处理。
示例:带命名空间的 XML
假设我们有如下 XML:
<ns2:user xmlns:ns2="http://www.example.com/users">
<ns2:id>101</ns2:id>
<ns2:name>李四</ns2:name>
</ns2:user>
对应的 Java 类可以这样定义:
import javax.xml.bind.annotation.*;
@XmlRootElement(name = "user", namespace = "http://www.example.com/users")
@XmlAccessorType(XmlAccessType.FIELD)
public class UserWithNamespace {
@XmlElement(namespace = "http://www.example.com/users")
private int id;
@XmlElement(namespace = "http://www.example.com/users")
private String name;
// 构造函数、getter、setter...
}
JAXB 会自动处理这些命名空间信息。
常见问题与注意事项
-
Java 9+ 模块化问题: 从 Java 9 开始,JAXB 不再是默认的内置模块,如果你使用 Java 9 或更高版本,并且开启了模块系统(
module-info.java),可能会遇到java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException错误。 解决方案:你需要手动添加 JAXB 依赖,如果你使用 Maven,在pom.xml中添加:<dependencies> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>2.3.1</version> </dependency> </dependencies>对于 Java 11+,
jaxb-runtime是必需的。 -
必须有默认构造函数: JAXB 在反序列化时需要通过反射调用类的无参(默认)构造函数来创建对象实例,你的 Java 类必须提供一个无参的构造函数,即使它是
public的。 -
循环引用问题: 如果对象图中有循环引用(A 对象包含 B 对象,B 对象又引用了 A 对象),直接序列化会导致无限循环,最终栈溢出。 解决方案:可以使用
@XmlTransient注解来标记某个字段,使其不被序列化。 -
性能: 对于非常大型或性能要求极高的场景,JAXB 可能不是最佳选择,可以考虑使用性能更高的 XML 库,如 Jackson 或 Gson(它们也支持 XML 格式,通常通过
jackson-dataformat-xml等扩展包实现)。
JAXB vs. Jackson XML
| 特性 | JAXB | Jackson XML (jackson-dataformat-xml) |
|---|---|---|
| 来源 | Java 标准 (JSR-222) | 第三方库 (Jackson 生态系统) |
| 依赖 | Java 6+ 内置 (Java 9+ 需手动添加) | 需要手动添加 Maven/Gradle 依赖 |
| 易用性 | 注解清晰,API 简单 | 注解丰富,与 JSON 库 API 风格一致 |
| 功能 | 功能强大,稳定 | 功能极其强大,性能通常更好,支持更多特性 |
| 集成 | 与 JAX-WS 等标准 Java EE 技术集成度高 | 与 Spring Boot 等现代框架集成度高,更灵活 |
- 如果你只需要一个简单、可靠的解决方案,并且不想引入额外依赖(在 Java 8 环境下),JAXB 是完美的选择。
- 如果你正在使用 Spring Boot,或者对性能、灵活性有更高要求,Jackson XML 是更现代、更通用的选择。
