我们需要结合 dom4j 和 XStream(或 JAXB 等其他库)来完成这个任务,XStream 是一个专门用于对象和 XML 之间相互转换的库,它非常灵活且易于使用。

下面我将为你提供一个完整的、分步的教程,展示如何使用 dom4j + XStream 来实现这个目标。
推荐使用 XStream (最简单、最常用)
XStream 的核心思想是将 Java 对象的字段名与 XML 的标签名进行映射,从而实现自动转换。
第 1 步:添加 Maven 依赖
在你的 pom.xml 文件中添加 dom4j 和 XStream 的依赖。
<dependencies>
<!-- dom4j 用于解析 XML -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.4</version>
</dependency>
<!-- XStream 用于 Java 对象和 XML 的相互转换 -->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.20</version>
</dependency>
<!-- XStream 需要 dom4j 或其他 XML 解析器,JDK 自带的 StAX 也可以,但 dom4j 是更常见的选择 -->
<!-- 如果使用 JDK 9+,可能需要添加这个依赖来访问 JAXB 模块 -->
<!--
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
-->
</dependencies>
第 2 步:创建 Java 实体类
创建一个与你的 XML 结构相匹配的 Java 类,XStream 会通过反射来访问这个类的字段。

假设我们有如下的 student.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student id="S001">
<name>张三</name>
<age>18</age>
<courses>
<course>数学</course>
<course>物理</course>
<course>化学</course>
</courses>
</student>
<student id="S002">
<name>李四</name>
<age>19</age>
<courses>
<course>语文</course>
<course>英语</course>
</courses>
</student>
</students>
对应的 Java 类如下:
Student.java
import java.util.List;
public class Student {
private String id;
private String name;
private int age;
private List<String> courses;
// 必须提供一个无参构造函数,XStream 需要它
public Student() {
}
// 为了方便查看结果,添加一个有参构造函数和 toString() 方法
public Student(String id, String name, int age, List<String> courses) {
this.id = id;
this.name = name;
this.age = age;
this.courses = courses;
}
// Getters and Setters (非常重要)
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public List<String> getCourses() { return courses; }
public void setCourses(List<String> courses) { this.courses = courses; }
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
", courses=" + courses +
'}';
}
}
Students.java (用于包装多个 Student 对象)

import java.util.List;
public class Students {
private List<Student> student;
// Getters and Setters
public List<Student> getStudent() {
return student;
}
public void setStudent(List<Student> student) {
this.student = student;
}
}
第 3 步:编写转换代码
我们可以编写主程序来读取 XML 并将其转换为 Students 对象。
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
public class XmlToObjectConverter {
public static void main(String[] args) {
// 1. 定义 XML 文件路径
File xmlFile = new File("src/main/resources/student.xml");
// 2. 创建 XStream 实例
// DomDriver 是 XStream 默认的驱动,它内部使用 DOM 解析器,我们可以直接使用 dom4j 的 SAXReader
XStream xstream = new XStream(new DomDriver());
// 3. 设置 XML 标签与 Java 类的别名
// 这一步很重要,它告诉 XStream <students> 标签对应 Students 类
xstream.alias("students", Students.class);
xstream.alias("student", Student.class);
// 如果想为 <course> 标签设置别名,也可以加上
// xstream.alias("course", String.class); // 对于简单类型,通常可以省略
// 4. (可选) 处理集合标签的别名
// 默认情况下,XStream 会将 List<Student> 映射为 <list> 标签。
// 如果你的 XML 中使用的是 <student> 作为集合项,可以设置以下属性来避免生成 <list> 标签。
xstream.addImplicitCollection(Students.class, "student", "student", Student.class);
// 解释:
// - Students.class: 在哪个类中查找这个集合
// - "student": 这个集合在 Students 类中的属性名 (getStudent())
// - "student": XML 中集合项的标签名
// - Student.class: 集合中元素的类型
// 5. (可选) 处理属性
// 如果想将 <student id="S001"> 中的 id 属性映射到 Student 类的 id 字段
xstream.useAttributeFor(Student.class, "id");
xstream.aliasField("id", Student.class, "id");
try {
// 6. 使用 dom4j 的 SAXReader 读取 XML 文件,得到 Document 对象
SAXReader reader = new SAXReader();
Document document = reader.read(xmlFile);
// 7. 将 Document 对象转换为字符串
// XStream 可以直接解析字符串形式的 XML
String xmlString = document.asXML();
// 8. 使用 XStream 将 XML 字符串转换为 Java 对象
Students students = (Students) xstream.fromXML(xmlString);
// 9. 验证结果
System.out.println("转换成功!获取到的学生列表:");
List<Student> studentList = students.getStudent();
for (Student student : studentList) {
System.out.println(student);
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
代码解释
XStream xstream = new XStream(new DomDriver());: 创建 XStream 实例,并指定使用 DOM 驱动来解析 XML。xstream.alias("students", Students.class);: 设置别名,这告诉 XStream,在解析 XML 时,遇到<students>标签,就创建一个Students类的实例,这可以让你在 XML 中使用更语义化的标签名(如<students>),而 Java 类名可以是Students。xstream.addImplicitCollection(...): 这是一个非常实用的技巧,当你的 XML 结构是<students><student>...</student><student>...</student></students>时,XStream 默认会生成<students><list><student>...</student></list></students>,通过这个设置,我们告诉 XStreamStudents类中的student列表是隐式的,其 XML 项的标签就是student,从而保持了原始 XML 的结构。xstream.useAttributeFor(...): 将 XML 元素的属性映射为 Java 对象的字段。<student id="S001">中的id属性值会直接设置到Student对象的id字段上。SAXReader: 这是 dom4j 的核心类,用于将 XML 文件解析成Document对象。document.asXML(): 将Document对象转换回 XML 字符串,虽然这里有点“绕”,但它清晰地展示了 dom4j 和 XStream 的分工:dom4j 负责读取和解析文件,XStream 负责格式转换。xstream.fromXML(xmlString): XStream 的核心方法,将 XML 字符串反序列化成 Java 对象。
纯 dom4j 手动转换 (不推荐,但有助于理解)
如果你不想引入 XStream,也可以完全使用 dom4j 手动进行转换,这种方式代码量更大,且 XML 结构稍有变化就需要修改代码,但能让你更深刻地理解 XML 解析的过程。
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class Dom4jManualConverter {
public static void main(String[] args) {
File xmlFile = new File("src/main/resources/student.xml");
SAXReader reader = new SAXReader();
List<Student> studentList = new ArrayList<>();
try {
Document document = reader.read(xmlFile);
// 获取根元素 <students>
Element rootElement = document.getRootElement();
// 获取所有的 <student> 元素
List<Element> studentElements = rootElement.elements("student");
for (Element studentElement : studentElements) {
// 创建 Student 对象
Student student = new Student();
// 1. 处理属性
student.setId(studentElement.attributeValue("id"));
// 2. 处理子元素
Element nameElement = studentElement.element("name");
if (nameElement != null) {
student.setName(nameElement.getText());
}
Element ageElement = studentElement.element("age");
if (ageElement != null) {
student.setAge(Integer.parseInt(ageElement.getText()));
}
// 3. 处理列表 <courses>
Element coursesElement = studentElement.element("courses");
if (coursesElement != null) {
List<String> courses = new ArrayList<>();
for (Element courseElement : coursesElement.elements("course")) {
courses.add(courseElement.getText());
}
student.setCourses(courses);
}
studentList.add(student);
}
// 验证结果
System.out.println("手动转换成功!获取到的学生列表:");
for (Student student : studentList) {
System.out.println(student);
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
手动转换的优缺点
- 优点:
- 不需要额外的依赖(除了 dom4j)。
- 对转换过程有完全的控制权。
- 缺点:
- 代码冗长: 需要为每个字段编写获取和设置代码。
- 脆弱性: XML 结构发生改变(
<name>标签改成了<fullName>),代码就需要相应修改,维护成本高。 - 重复性高: 如果有多个相似的 XML 结构,会产生大量重复代码。
| 特性 | 方案一 (dom4j + XStream) | 方案二 (纯 dom4j 手动转换) |
|---|---|---|
| 易用性 | 非常高,几行代码即可完成 | 低,需要大量样板代码 |
| 维护性 | 高,通过别名和注解适应结构变化 | 低,XML 结构变化需修改大量代码 |
| 灵活性 | 高,支持复杂映射、转换器等 | 低,只能处理标准映射 |
| 依赖 | 需要额外引入 XStream 库 | 只需 dom4j |
| 适用场景 | 绝大多数项目,特别是需要处理复杂或频繁变化的 XML 时 | 简单、一次性、或对依赖有严格限制的小任务 |
在实际开发中,强烈推荐使用方案一(dom4j + XStream),它将“文件解析”和“对象转换”的职责清晰分离,代码简洁、健壮且易于维护,是处理 XML 与 Java 对象转换的行业标准做法之一。
