首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Dom4j 解析 XML 完整實戰指南:讀取、遍歷、創建、XPath 與生產級優化

     編輯:Java 程式設計入門

前言:為什麼選擇 Dom4j?

Java 原生 DOM/SAX API 雖標準,但代碼冗長、異常處理繁瑣、命名空間支持弱。Dom4j 作為第三方庫,憑藉鏈式 API、XPath 原生支持、靈活的輸出格式化、優異的效能,長期佔據 Java XML 處理首選地位。本文通過完整示例,涵蓋讀取遍歷(List/Iterator 兩種風格)、創建寫入、XPath 查詢、命名空間處理、生產級優化配置。

一、Maven 依賴與核心類

<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.4</version> <!-- 最新穩定版 -->
</dependency>

核心類:

  • SAXReader — 解析器入口,支援文件、InputStream、URL、Reader。
  • Document — 文檔根節點,getRootElement() 獲取根元素。
  • Element — 元素節點,提供 elements()、element()、attributeValue()、elementText() 等便捷方法。
  • DocumentHelper — 程序化創建文檔的工廠。
  • XMLWriter + OutputFormat — 格式化輸出。

二、示例 XML 文檔

<?xml version="1.0" encoding="UTF-8"?>
<books>
    <book id="001">
        <title>Harry Potter</title>
        <author>J K. Rowling</author>
    </book>
    <book id="002">
        <title>Learning XML</title>
        <author>Erik T. Ray</author>
    </book>
</books>

三、遍歷讀取:兩種主流風格

風格一:List 集合遍歷(推薦,代碼簡潔)

public static void parseWithList(String filePath) throws Exception {
    SAXReader reader = new SAXReader();
    Document document = reader.read(new File(filePath));
    Element root = document.getRootElement();
    
    // 獲取所有子元素 List
    List<Element> books = root.elements(); 
    
    for (Element book : books) {
        // 已知屬性名直接取值
        String id = book.attributeValue("id");
        System.out.println("id: " + id);
        
        // 已知子元素名直接取文本
        System.out.println("title: " + book.elementText("title"));
        System.out.println("author: " + book.elementText("author"));
        System.out.println();
    }
}

優點:elements() 返回泛型 List,支援增強 for、Stream API、Lambda 處理。

風格二:Iterator 迭代器遍歷(舊版本/內存敏感)

public static void parseWithIterator(String filePath) throws Exception {
    SAXReader reader = new SAXReader();
    Document document = reader.read(new File(filePath));
    Element root = document.getRootElement();
    
    Iterator<Element> it = root.elementIterator(); // 或 root.elementIterator("book")
    while (it.hasNext()) {
        Element book = it.next();
        System.out.println("id: " + book.attributeValue("id"));
        System.out.println("title: " + book.elementText("title"));
        System.out.println("author: " + book.elementText("author"));
        System.out.println();
    }
}

優點:舊版本 Dom4j (1.x) 返回原始 Iterator,無泛型;大文檔時可邊讀邊處理釋放引用(需配合 SAX 事件模式)。

未知結構時的通用遍歷

// 遞歸打印任意 XML 結構
public static void printTree(Element element, int depth) {
    String indent = "  ".repeat(depth);
    System.out.println(indent + "<" + element.getName() + ">");
    
    // 屬性
    for (Attribute attr : element.attributes()) {
        System.out.println(indent + "  @" + attr.getName() + "=" + attr.getValue());
    }
    // 文本內容
    String text = element.getTextTrim();
    if (!text.isEmpty()) {
        System.out.println(indent + "  #text: " + text);
    }
    // 子元素遞歸
    for (Element child : element.elements()) {
        printTree(child, depth + 1);
    }
}

四、XPath 查詢:精準定位的利器

public static void xpathDemo(Document doc) {
    // 1. 選擇所有 book 的 title 文本
    List<Node> titles = doc.selectNodes("//book/title/text()");
    titles.forEach(n -> System.out.println("Title: " + n.getText()));
    
    // 2. 選擇 id=002 的 book 元素
    Element book002 = (Element) doc.selectSingleNode("//book[@id='002']");
    if (book002 != null) {
        System.out.println("Found: " + book002.elementText("title"));
    }
    
    // 3. 統計 book 數量
    Number count = (Number) doc.selectObject("count(//book)");
    System.out.println("Total books: " + count.intValue());
    
    // 4. 命名空間查詢(需註冊前綴)
    // Map<String, String> ns = Map.of("ns", "https://example.com/ns");
    // doc.selectNodes("//ns:book", ns);
}

XPath 支援完整 1.0 標準:路徑、謂語、軸、函數(count, sum, contains, starts-with 等)。

五、創建 XML 文檔並格式化輸出

public static void createXml(String outputPath) throws Exception {
    // 1. 創建文檔對象
    Document doc = DocumentHelper.createDocument();
    
    // 2. 構建樹結構
    Element books = doc.addElement("books");
    
    Element book1 = books.addElement("book").addAttribute("id", "001");
    book1.addElement("title").setText("Harry Potter");
    book1.addElement("author").setText("J K. Rowling");
    
    Element book2 = books.addElement("book").addAttribute("id", "002");
    book2.addElement("title").setText("Learning XML");
    book2.addElement("author").setText("Erik T. Ray");
    
    // 3. 格式化輸出配置
    OutputFormat format = OutputFormat.createPrettyPrint(); // 縮排、換行
    format.setEncoding("UTF-8");                            // 指定編碼
    format.setIndent("  ");                                 // 兩空格縮排
    format.setNewlines(true);                               // 換行
    // format.setTrimText(true); // 去除文本節點首尾空白
    
    // 4. 寫入文件
    try (FileOutputStream fos = new FileOutputStream(outputPath);
         XMLWriter writer = new XMLWriter(fos, format)) {
        writer.write(doc);
    }
    
    // 5. 寫入字串(用於網絡傳輸/日誌)
    StringWriter sw = new StringWriter();
    try (XMLWriter writer = new XMLWriter(sw, format)) {
        writer.write(doc);
    }
    String xmlString = sw.toString();
}

輸出效果與示例 XML 完全一致,含 XML 聲明、縮排、UTF-8 編碼。

六、命名空間處理:避開的大坑

<root xmlns="https://default.ns" xmlns:ns="https://custom.ns">
    <ns:item id="1">value</ns:item>
</root>
// 1. 解析時:預設命名空間不影響 element("name") 查找(需用 QName)
Element root = doc.getRootElement();
QName itemQName = QName.get("item", "https://custom.ns"); // 或者
// QName itemQName = new QName("item", Namespace.get("https://custom.ns"));
Element item = root.element(itemQName);

// 2. XPath 查詢:必須註冊前綴映射
Map<String, String> nsMap = new HashMap<>();
nsMap.put("def", "https://default.ns");
nsMap.put("ns", "https://custom.ns");
List<Node> items = doc.selectNodes("//ns:item", nsMap);
// 注意:無前綴的默認命名空間在 XPath 中必須給一個前綴(如 def)

七、生產級優化與避坑指南

場景問題解決方案
大文件 (>50MB)DOM 模式全量載入導致 OOM使用 SAXReader + ElementHandler 推模式處理,或遷移至 StAX/木犀
高併發解析SAXReader 非線程安全每線程 new SAXReader(),或使用 ThreadLocal 復用
外部實體攻擊 (XXE)默認解析 DTD/外部實體reader.setFeature("https://apache.org/xml/features/disallow-doctype-decl", true); reader.setFeature("https://xml.org/sax/features/external-general-entities", false);
編碼亂碼文件聲明 encoding 與實際不符強制指定 reader.setEncoding("UTF-8");輸出時 format.setEncoding("UTF-8")
頻繁創建 Document對象分配壓力對象池復用 Document/Element(需手動 detach 清理)
屬性值含特殊字符 (< & >)手動拼接 XML 導致注入永遠用 element.setText() / addAttribute(),Dom4j 自動轉義

八、現代替代方案對比

庫模式優點缺點適用場景
Dom4jDOMAPI 友好、XPath 強、格式化好全量載入、維護緩慢中小配置文件、報表生成、接口組裝
JDOM2DOM純 Java、泛型完善效能稍弱教學、輕量項目
XOMDOM極度嚴格、防 XXE學習曲線陡高安全需求
StAX (JSR-173)流式標準、低內存、雙向游標API 低級、手動管理狀態大文件、高吞吐、遷移項目
WoodstoxStAX 實現快、穩、功能全第三方依賴生產級 StAX 首選
Jackson XMLData BindingPOJO 映射、注解驅動靈活性弱、需 SchemaREST API、配置綁定

結語

Dom4j 雖非最新技術,但憑藉優雅的 API、強大的 XPath、成熟的生態,仍是 Java XML 處理的「瑞士軍刀」。掌握 SAXReader 讀取、DocumentHelper 創建、selectNodes XPath 查詢、OutputFormat 格式化輸出四大核心能力,並針對大文件、XXE、編碼、併發做好防禦,即可應對 90% 的業務場景。新架構設計時,建議根據文件大小、性能要求、團隊熟悉度,在 Dom4j、StAX、Jackson XML 中選型,避免「拿著錘子找釘子」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved