dom4j 是一个非常流行、功能强大且性能优异的 Java XML 处理框架,它使用的是 DOM(文档对象模型)的思想,将整个 XML 文件读入内存,形成一个树形结构,方便我们遍历和操作。

准备工作:添加 dom4j 依赖
你需要在你的项目中添加 dom4j 的依赖,如果你使用 Maven,这是最简单的方式。
Maven (pom.xml)
在你的 pom.xml 文件中添加以下依赖:
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.4</version> <!-- 建议使用较新版本 -->
</dependency>
如果你不使用 Maven,可以从 Maven 中央仓库 下载 JAR 文件,并将其添加到你的项目的类路径中。
示例 XML 文件
为了演示,我们创建一个名为 books.xml 的文件。

books.xml
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="children">
<title lang="en">Harry Potter</title>
<author>J.K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="web">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
</bookstore>
核心概念
在开始编码前,了解 dom4j 的几个核心接口和类非常重要:
Document: 代表整个 XML 文档,它是 XML 树的根节点。Element: 代表 XML 中的一个元素(标签),如<bookstore>,<book>,它是Node的主要子接口。Attribute: 代表元素的属性,如category="children"。Node:dom4j中所有节点的基接口,包括Element,Attribute,Document等。Element的方法:element(String name): 获取指定名称的第一个子元素。elements(): 获取所有子元素的列表。elements(String name): 获取所有指定名称的子元素列表。attribute(String name): 获取指定名称的属性。attributeValue(String name): 获取指定名称属性的值(常用)。getText(): 获取元素的文本内容。getTextTrim(): 获取并去除文本内容两端的空白字符(常用)。
读取 XML 的完整代码示例
下面是一个完整的 Java 程序,它读取 books.xml 文件,并以不同方式遍历和打印其中的数据。
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
public class Dom4jReaderDemo {
public static void main(String[] args) {
try {
// 1. 创建 SAXReader 对象,它是读取 XML 的核心工具
SAXReader reader = new SAXReader();
// 2. 加载 XML 文件,获取 Document 对象
// 请确保 books.xml 文件位于项目根目录下,或提供正确的路径
Document document = reader.read(new File("books.xml"));
// 3. 获取 XML 文档的根元素
Element rootElement = document.getRootElement();
System.out.println("根元素是: " + rootElement.getName());
System.out.println("\n--- 方式一:遍历所有 book 元素 ---");
// 4. 获取根元素下所有名为 "book" 的子元素列表
List<Element> bookList = rootElement.elements("book");
for (Element book : bookList) {
// 5. 获取元素的属性
String category = book.attributeValue("category");
System.out.println("书籍类别: " + category);
// 6. 获取子元素及其文本内容
String title = book.element("title").getTextTrim();
String author = book.element("author").getTextTrim();
String year = book.element("year").getTextTrim();
String price = book.element("price").getTextTrim();
System.out.println(" - 书名: " + title);
System.out.println(" - 作者: " + author);
System.out.println(" - 年份: " + year);
System.out.println(" - 价格: " + price);
System.out.println("----------------------------------");
}
System.out.println("\n--- 方式二:使用迭代器深度遍历所有节点 ---");
// 7. 使用迭代器遍历所有节点,可以处理更复杂的结构
Iterator<Node> iterator = document.nodeIterator();
while (iterator.hasNext()) {
Node node = iterator.next();
// 只处理元素节点
if (node instanceof Element) {
Element element = (Element) node;
System.out.println("当前节点名称: " + element.getName());
// 递归处理子节点
printChildNodes(element, 1);
}
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* 递归打印所有子节点
* @param element 父元素
* @param level 缩进层级,用于格式化输出
*/
private static void printChildNodes(Element element, int level) {
String indent = " ".repeat(level); // Java 11+ 的 repeat 方法
List<Element> children = element.elements();
if (children.isEmpty()) {
// 如果是叶子节点,打印其文本内容
System.out.println(indent + "└── 文本内容: " + element.getTextTrim());
} else {
for (Element child : children) {
System.out.println(indent + "├── " + child.getName());
printChildNodes(child, level + 1);
}
}
}
}
代码解释:
-
SAXReader reader = new SAXReader();创建SAXReader实例,这是解析 XML 的入口。
(图片来源网络,侵删) -
reader.read(new File("books.xml"));read()方法会解析指定的 XML 文件,并返回一个Document对象,代表整个 XML 文档树。 -
document.getRootElement();从Document对象中获取根元素,在我们的例子中就是<bookstore>。 -
rootElement.elements("book");获取根元素下所有名为book的子元素,返回一个List<Element>。 -
book.attributeValue("category");直接获取book元素的category属性的值。 -
book.element("title").getTextTrim();获取book元素下的第一个title子元素,并调用getTextTrim()获取其文本内容(getText()也会获取,但getTextTrim()会去掉首尾多余的空格和换行符,更常用)。 -
迭代器遍历 这种方式更通用,不依赖于元素名称。
document.nodeIterator()获取文档中所有节点的迭代器,通过instanceof判断节点类型,可以分别处理元素、属性、文本等不同类型的节点,递归调用printChildNodes可以实现深度优先遍历。
处理更复杂的情况
1 处理命名空间
XML 文件使用了命名空间,读取方式会稍有不同。
books_with_ns.xml
<bookstore xmlns="http://www.example.com/books">
<book category="children">
<title lang="en">Harry Potter</title>
<author>J.K. Rowling</author>
</book>
</bookstore>
处理代码:
// ... 前面的代码相同
Document document = reader.read(new File("books_with_ns.xml"));
// 定义命名空间
Namespace ns = new Namespace("ns", "http://www.example.com/books");
// 获取根元素时需要指定命名空间
Element rootElement = document.getRootElement(ns);
System.out.println("根元素是: " + rootElement.getName());
// 获取子元素时也需要指定命名空间
List<Element> bookList = rootElement.elements(new QName("book", ns));
// ... 后续处理相同
2 使用 XPath 快速查询
dom4j 对 XPath 提供了非常好的支持,可以让你用非常简洁的语法直接定位到任意节点。
// ... 前面的代码相同,获取 document 对象
// 使用 XPath 获取所有书的作者
List<Node> authors = document.selectNodes("//book/author");
System.out.println("\n--- 使用 XPath 查询所有作者 ---");
for (Node node : authors) {
System.out.println(node.getText());
}
// 使用 XPath 获取第一本书的价格
Node firstPriceNode = document.selectSingleNode("//book[1]/price");
if (firstPriceNode != null) {
System.out.println("\n--- 使用 XPath 查询第一本书的价格 ---");
System.out.println(firstPriceNode.getText());
}
| 任务 | dom4j 方法/代码 |
|---|---|
| 创建解析器 | SAXReader reader = new SAXReader(); |
| 读取文件 | Document doc = reader.read(new File("path/to/file.xml")); |
| 获取根元素 | Element root = doc.getRootElement(); |
| 获取所有子元素 | List<Element> children = root.elements(); |
| 获取指定名称的子元素 | List<Element> books = root.elements("book"); |
| 获取第一个指定子元素 | Element title = book.element("title"); |
| 获取元素文本 | String text = title.getTextTrim(); |
| 获取属性 | Attribute attr = book.attribute("category"); |
| 获取属性值 | String value = book.attributeValue("category"); |
| XPath 查询 | List<Node> nodes = doc.selectNodes("//author"); |
dom4j 是一个非常灵活和强大的工具,掌握它可以让你在处理 XML 时游刃有余,对于大多数 Java 应用程序来说,它是处理 XML 的首选库之一。
