使用 Dom4j 解析 XML 📒

3,729 阅读3分钟

Dom4j

这里使用的目前最新的 dom4j-2.1.3 版本链接

对应的 maven :

<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.3</version>
</dependency>

注意,dom4j 的 2.1.x 版本对 jdk 的要求是 Java 8+。

Dom4j会将 XML 看成一个 Document 对象,将 XML 标签看成 Element 对象。

假设我们有一个 XML 文件:students.xml ,内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<students>
    <student no="001">
        <name>张三</name>
        <age>18</age>
        <score>96</score>
        <parents>
            <father>阿强</father>
            <mother>阿珍</mother>
        </parents>
    </student>
    <student no="002">
        <name>李四</name>
        <age>19</age>
        <score>99</score>
        <parents>
            <father>阿贵</father>
            <mother>阿莲</mother>
        </parents>
    </student>
</students>

读取

首先,我们需要实例化 SAXReader 类,它是读取 XML 文件的核心类。调用其 read 方法读取 xml 文件,会生成一个 Document 对象。

String file = "/path/to/xml/test.xml";

SAXReader reader = new SAXReader();
Document document = reader.read(file);

read 方法会抛出一个 DocumentException 异常。

我们可以使用 Document 对象中的 getRootElement 方法获取根节点,即此处的 <students> 标签:

Element root = document.getRootElement();

注意,所有的 XML 标签都对应着一个 Element 对象,即使是文档的根节点。

在 students.xml 文件中,<students> 根节点有两个 <student> 节点,我们可以使用 Element 对象的 elements 方法获取一组相同标签的集合:

List<Element> students = root.elements("student");

返回的集合中的每个元素仍然是 Element 类型的对象。

接下来,使用 for 循环遍历这个集合。

for (Element student : students) {
    // element 方法用于获取唯一的子节点对象
    Element nameElement = student.element("name");
    // getText 方法用于获取标签文本
    String name = nameElement.getText();
    System.out.println("name: " + name);
}

对于一个 Element 对象,如果想要获取其子元素,则调用其 element 方法,如:

Element nameElement = student.element("name");

Element 对象还有一个 getText 方法用于获取标签文本,

String name = nameElement.getText();

当然,你也可以直接获取子元素的文本,只需要调用 Element 对象的 elementText 方法:

// elementText 方法用于获取指定标签的文本
String age = student.elementText("age");
System.out.println("age: " + age);

对于一个元素的属性来说,可以使用 attribute 方法获取,该方法返回一个 Attribute 对象,调用 getText 方法可以获取属性的值:

Attribute noAttr = student.attribute("no");
String no = noAttr.getText();
System.out.println("no: " + no);

完整的代码示例如下:

public class Main {
    public static void readXML() throws DocumentException {
        String file = "/path/to/xml/test.xml";
        
        SAXReader reader = new SAXReader();
        Document document = reader.read(file);
        // 获取 XML 文档的根节点,即 <students> 标签
        Element root = document.getRootElement();
        // elements 方法用于获取指定的标签集合
        List<Element> students = root.elements("student");

        for (Element student : students) {
            // attribute 方法用于获取标签属性
            Attribute noAttr = student.attribute("no");
            String no = noAttr.getText();
            System.out.println("no: " + no);

            // element 方法用于获取唯一的子节点对象
            Element nameElement = student.element("name");
            // getText 方法用于获取标签文本
            String name = nameElement.getText();
            System.out.println("name: " + name);

            // elementText 方法用于获取指定标签的文本
            String age = student.elementText("age");
            System.out.println("age: " + age);

            String score = student.elementText("score");
            System.out.println("score: " + score);

            Element parents = student.element("parents");
            String father = parents.elementText("father");
            String mother = parents.elementText("mother");

            System.out.println("father: " + father);
            System.out.println("mother: " + mother);
            System.out.println("---------");
        }
    }

    public static void main(String[] args) {
        try {
            readXML();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}

输出:

no: 001
name: 张三
age: 18
score: 96
father: 阿强
mother: 阿珍
---------
no: 002
name: 李四
age: 19
score: 99
father: 阿贵
mother: 阿莲
---------

写入

除了读取,我们还需要在 XML 文档中新增一些节点,以及将新生成的 XML 写入文件中。

新增节点,使用 Element 对象的 addElement 方法,该方法返回一个新生成的 Element 对象。我们可以对新生成的 Element 设置文本内容,或者再创建新的子节点:

Element root = document.getRootElement();
Element student = root.addElement("student");
student.addElement("name").setText("王五");

之后,当我们需要保存至文件的时候,只需要调用 Document 对象的 write 方法,传入相应的 Writer 输出流对象即可:

FileOutputStream fos = new FileOutputStream("/path/to/xml/test2.xml");
Writer writer = new OutputStreamWriter(fos);
document.write(writer);
writer.close();
fos.close();

当出现乱码时,可以尝试指定编码:

Writer writer = new OutputStreamWriter(fos, "UTF-8");

完整的代码示例如下:

public static void writeXML() throws DocumentException, IOException {
    String file = "/path/to/xml/test.xml";
    // SAXReader 是读取 XML 文件的核心类,用于将 XML 解析后以树的形式保存在内存中
    SAXReader reader = new SAXReader();
    Document document = reader.read(file);

    Element root = document.getRootElement();
    Element student = root.addElement("student");
    student.addAttribute("no", "003");
    student.addElement("name").setText("王五");
    student.addElement("age").setText("20");
    student.addElement("score").setText("100");
    Element parents = student.addElement("parents");
    parents.addElement("father").setText("阿发");
    parents.addElement("mother").setText("阿梅");

    FileOutputStream fos = new FileOutputStream("/path/to/xml/test2.xml");
    Writer writer = new OutputStreamWriter(fos);
    document.write(writer);
    writer.close();
    fos.close();
}