杰瑞科技汇

Java DOM如何高效解析XML?

什么是 DOM 解析?

DOM (Document Object Model, 文档对象模型) 是一种与平台和语言无关的接口,它将 XML 文档(或 HTML 文档)解析成一个树形结构,这个树由节点(Node)组成,每个节点代表 XML 文档中的一个元素、属性、文本或其他部分。

Java DOM如何高效解析XML?-图1
(图片来源网络,侵删)

核心思想: 将整个 XML 文档一次性加载到内存中,构建一个完整的树形结构,你可以通过遍历这棵树来访问、查询、修改或删除文档中的任何部分。

优点:

  • 结构清晰: 树形结构直观,易于理解和操作。
  • 随机访问: 可以随时访问树中的任意节点,无需像 SAX 那样从头开始遍历。
  • 支持修改: 可以方便地修改 XML 文档的结构和内容。

缺点:

  • 内存消耗大: 对于大型 XML 文件,将其全部加载到内存中可能会消耗大量资源,甚至导致内存溢出(OutOfMemoryError)。
  • 性能较低: 因为需要一次性加载整个文件,所以解析速度相对较慢。

DOM 解析器非常适合处理小型到中等规模的 XML 文件。

Java DOM如何高效解析XML?-图2
(图片来源网络,侵删)

准备工作:XML 示例文件

我们使用一个名为 students.xml 的文件作为示例。

students.xml

<?xml version="1.0" encoding="UTF-8"?>
<students>
    <student id="S001">
        <name>张三</name>
        <age>20</age>
        <gender>男</gender>
    </student>
    <student id="S002">
        <name>李四</name>
        <age>22</age>
        <gender>女</gender>
    </student>
    <student id="S003">
        <name>王五</name>
        <age>21</age>
        <gender>男</gender>
    </student>
</students>

DOM 解析步骤

使用 Java DOM 解析 XML 主要分为以下几步:

  1. 获取 DocumentBuilderFactory 实例:这是一个工厂类,用于创建 DocumentBuilder
  2. 获取 DocumentBuilder 实例:这是实际的解析器。
  3. 解析 XML 文件,获取 Document 对象Document 对象代表了整个 XML 文档的树形结构。
  4. 获取文档的根节点:通常是 <students>
  5. 遍历节点树,提取数据:通过 getElementsByTagName()getChildNodes() 等方法查找和访问节点。
  6. 获取节点的属性和文本内容
  7. (可选)修改 XML:可以创建新节点、修改节点内容或属性,然后将修改后的 Document 对象写回 XML 文件。

完整代码示例

下面是一个完整的 Java 程序,演示如何解析 students.xml 文件,并打印出每个学生的信息。

Java DOM如何高效解析XML?-图3
(图片来源网络,侵删)

DOMParserDemo.java

import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
public class DOMParserDemo {
    public static void main(String[] args) {
        try {
            // 1. 创建一个 DocumentBuilderFactory 对象
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            // 2. 创建一个 DocumentBuilder 对象
            DocumentBuilder builder = factory.newDocumentBuilder();
            // 3. 解析 XML 文件,得到一个 Document 对象(整个 XML 文档的树形结构)
            //    请确保 students.xml 文件位于项目根目录下,或者提供正确的文件路径
            Document document = builder.parse(new File("students.xml"));
            // 4. 获取文档的根节点 <students>
            Element root = document.getDocumentElement();
            // 打印根节点名称
            System.out.println("根节点: " + root.getNodeName());
            // 5. 获取所有名为 "student" 的节点列表
            NodeList studentList = root.getElementsByTagName("student");
            System.out.println("\n--- 开始解析学生信息 ---");
            // 6. 遍历 studentList
            for (int i = 0; i < studentList.getLength(); i++) {
                // 将每个节点转换为 Element 元素,以便操作其属性
                Element studentElement = (Element) studentList.item(i);
                // 7. 获取节点的属性 "id"
                String id = studentElement.getAttribute("id");
                System.out.println("学生ID: " + id);
                // 8. 获取子节点 <name>, <age>, <gender>
                //    getElementsByTagName() 会在该元素的子树中查找
                NodeList nameList = studentElement.getElementsByTagName("name");
                NodeList ageList = studentElement.getElementsByTagName("age");
                NodeList genderList = studentElement.getElementsByTagName("gender");
                // 9. 获取第一个子节点的文本内容
                //    注意:XML 标签内的文本是作为该元素的子 TextNode 存在的
                String name = nameList.item(0).getTextContent();
                String age = ageList.item(0).getTextContent();
                String gender = genderList.item(0).getTextContent();
                System.out.println("  姓名: " + name);
                System.out.println("  年龄: " + age);
                System.out.println("  性别: " + gender);
                System.out.println("------------------------");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码解析

  1. DocumentBuilderFactory.newInstance(): 获取一个工厂实例,这是一个标准的工厂模式实现。
  2. factory.newDocumentBuilder(): 使用工厂创建解析器 DocumentBuilder
  3. builder.parse(new File("students.xml")): 这是核心步骤,解析器读取文件并构建内存中的 DOM 树,返回一个 Document 对象。
  4. document.getDocumentElement(): 获取文档的根元素,即 <students>
  5. root.getElementsByTagName("student"): 从根节点开始,查找所有名为 "student" 的元素,返回一个 NodeList
  6. for (int i = 0; i < studentList.getLength(); i++): 遍历 NodeList,处理每个 <student> 节点。
  7. studentElement.getAttribute("id"): 获取当前 <student> 元素的 id 属性的值。
  8. studentElement.getElementsByTagName("name"): 在当前 <student> 元素内部查找 <name> 子节点。
  9. nameList.item(0).getTextContent(): item(0) 获取 NodeList 中的第一个节点(XML 中一个标签名是唯一的,所以只有一个)。getTextContent() 获取该节点及其所有子节点的文本内容,对于 <name>张三</name>,它会返回 "张三"。

运行结果

假设 students.xml 文件和 DOMParserDemo.java 在同一个目录下,运行 DOMParserDemo.java,输出如下:

根节点: students
--- 开始解析学生信息 ---
学生ID: S001
  姓名: 张三
  年龄: 20
  性别: 男
------------------------
学生ID: S002
  姓名: 李四
  年龄: 22
  性别: 女
------------------------
学生ID: S003
  姓名: 王五
  年龄: 21
  性别: 男
------------------------

进阶:修改 XML 并保存

DOM 的一个强大之处在于可以修改文档,下面是一个示例,展示如何添加一个新学生,并将修改后的内容保存到新文件中。

ModifyAndSaveXML.java

import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
public class ModifyAndSaveXML {
    public static void main(String[] args) {
        try {
            // 1. 解析现有文件
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(new File("students.xml"));
            Element root = document.getDocumentElement();
            // 2. 创建新学生节点 <student>
            Element newStudent = document.createElement("student");
            newStudent.setAttribute("id", "S004");
            // 3. 创建新学生的子节点 <name>, <age>, <gender>
            Element name = document.createElement("name");
            name.setTextContent("赵六");
            Element age = document.createElement("age");
            age.setTextContent("23");
            Element gender = document.createElement("gender");
            gender.setTextContent("女");
            // 4. 将子节点添加到 <student> 节点
            newStudent.appendChild(name);
            newStudent.appendChild(age);
            newStudent.appendChild(gender);
            // 5. 将新学生节点添加到根节点 <students>
            root.appendChild(newStudent);
            // 6. 将修改后的 DOM 树写入新文件
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 格式化输出
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
            DOMSource source = new DOMSource(document);
            StreamResult result = new StreamResult(new File("students_modified.xml"));
            transformer.transform(source, result);
            System.out.println("XML 文件已成功修改并保存为 students_modified.xml");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行后生成的新文件 students_modified.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<students>
    <student id="S001">
        <name>张三</name>
        <age>20</age>
        <gender>男</gender>
    </student>
    <student id="S002">
        <name>李四</name>
        <age>22</age>
        <gender>女</gender>
    </student>
    <student id="S003">
        <name>王五</name>
        <age>21</age>
        <gender>男</gender>
    </student>
    <student id="S004">
        <name>赵六</name>
        <age>23</age>
        <gender>女</gender>
    </student>
</students>

DOM vs. SAX (简短对比)

特性 DOM (Document Object Model) SAX (Simple API for XML)
工作方式 将整个 XML 文档加载到内存,构建树形结构。 事件驱动,逐行读取 XML 文件,不保存文档内容。
内存占用 ,与文件大小成正比。 ,内存占用恒定,适合大文件。
访问方式 随机访问,可以前后遍历。 顺序访问,只能从头到尾一次。
修改能力 可以方便地修改、添加、删除节点。 不可以修改文档,只能读取数据。
解析速度 较慢。 较快。
适用场景 小型/中型 XML 文件,需要频繁访问或修改文档内容。 大型 XML 文件,只需要读取一次数据,对内存敏感。

Java 原生的 DOM API 功能强大且灵活,是处理 XML 的标准方式之一,对于中小型 XML 文件,它是一个非常直观和有效的选择,如果你的应用需要处理 GB 级别的大型 XML 文件,或者内存非常有限,那么应该考虑使用 SAX 或 StAX 解析器,在现代 Java 开发中,也有一些更高级的库(如 JAXB 用于 XML 与 Java 对象的绑定,JDOMDOM4J 作为对标准 DOM 的增强和简化),但理解基础的 DOM 原理仍然是至关重要的。

分享:
扫描分享到社交APP
上一篇
下一篇