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

DOM4J 讀寫 XML 完整實戰:XPath、命名空間、大文件流式處理

     編輯:Java 相關知識

DOM4J 是 Java 操作 XML 的經典庫,支援 DOM 樹、XPath、SAX/StAX 事件流多種模式。本文涵蓋讀寫基礎、XPath 查詢、命名空間處理、大文件流式讀寫、HTML 解析、常見異常排查。

Maven 依賴

<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    2.1.4
</dependency>

<dependency>
    <groupId>jaxen</groupId>
    jaxen
    1.2.4
</dependency>

核心讀取模式對比

模式記憶體速度適用場景
DOM (SAXReader.read)載入整檔快< 50MB、需隨機存取、XPath 查詢
SAX 事件流恆定快大文件、單向讀取、不需修改
StAX (XMLStreamReader)恆定最快超大文件、高效能、需精細控制

DOM 模式完整範例 (含 XPath、命名空間)

public class Dom4jDemo {
    public static void main(String[] args) throws Exception {
        // 1. 讀取
        SAXReader reader = new SAXReader();
        // 支援命名空間
        reader.setFeature("https://apache.org/xml/features/namespace-prefixes", true);
        Document doc = reader.read(new File("books.xml"));

        // 2. XPath 查詢 (需註冊命名空間)
        Map<String, String> ns = Map.of("bk", "https://example.com/book");
        XPath xpath = doc.createXPath("//bk:book[bk:price > 30]", ns);
        List<Node> expensive = xpath.selectNodes(doc);
        for (Node n : expensive) {
            Element book = (Element) n;
            System.out.println("書名: " + book.elementText("title"));
            System.out.println("價格: " + book.elementText("price"));
            System.out.println("ISBN 屬性: " + book.attributeValue("isbn"));
        }

        // 3. 修改並寫入
        Element root = doc.getRootElement();
        Element newBook = root.addElement("book").addAttribute("isbn", "999-999");
        newBook.addElement("title").setText("DOM4J 實戰");
        newBook.addElement("price").setText("59");

        // 格式化輸出
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("UTF-8");
        XMLWriter writer = new XMLWriter(new FileOutputStream("books_out.xml"), format);
        writer.write(doc);
        writer.close();
    }
}

大文件流式讀取 (SAX 事件驅動)

public class LargeXmlHandler extends DefaultHandler {
    private String currentElement;
    private final List<Book> books = new ArrayList<>();
    private Book currentBook;

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attrs) {
        if ("book".equals(qName)) {
            currentBook = new Book();
            currentBook.setIsbn(attrs.getValue("isbn"));
        }
        currentElement = qName;
    }

    @Override
    public void characters(char[] ch, int start, int length) {
        if (currentBook == null) return;
        String value = new String(ch, start, length).trim();
        if (value.isEmpty()) return;
        switch (currentElement) {
            case "title" -> currentBook.setTitle(value);
            case "price" -> currentBook.setPrice(Double.parseDouble(value));
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) {
        if ("book".equals(qName) && currentBook != null) {
            books.add(currentBook);
            currentBook = null;
        }
        currentElement = null;
    }

    public List<Book> getBooks() { return books; }
}

// 使用
SAXReader reader = new SAXReader();
LargeXmlHandler handler = new LargeXmlHandler();
reader.setContentHandler(handler);
reader.read(new File("huge_books.xml"));
List<Book> books = handler.getBooks();

流式寫入大文件 (StAX XMLStreamWriter)

XMLOutputFactory factory = XMLOutputFactory.newFactory();
try (XMLStreamWriter writer = factory.createXMLStreamWriter(
        new FileOutputStream("huge_out.xml"), "UTF-8")) {

    writer.writeStartDocument("UTF-8", "1.0");
    writer.writeStartElement("catalog");

    for (int i = 1; i <= 1_000_000; i++) {
        writer.writeStartElement("book");
        writer.writeAttribute("isbn", "ISBN-" + i);
        writer.writeStartElement("title"); writer.writeCharacters("Book " + i); writer.writeEndElement();
        writer.writeStartElement("price"); writer.writeCharacters(String.valueOf(10 + i % 100)); writer.writeEndElement();
        writer.writeEndElement(); // book
    }

    writer.writeEndElement(); // catalog
    writer.writeEndDocument();
}

HTML 解析 (HTMLParser)

// DOM4J 內建 HTML 解析器 (基於 JTidy)
SAXReader htmlReader = new SAXReader(new org.dom4j.io.HTMLReader());
Document htmlDoc = htmlReader.read(new URL("https://example.com").openStream());
// 之後同樣用 XPath 查詢
List<Node> links = htmlDoc.selectNodes("//a/@href");

常見異常與解決

異常原因解決
NoClassDefFoundError: jaxen/XPath缺少 jaxen 依賴加入 jaxen jar
InvalidXPathException命名空間未註冊`xpath.setNamespaceURIs(Map)`
記憶體溢出 (DOM 讀大檔)樹模型佔用過大改用 SAX/StAX 流式
編碼亂碼XML 声明 encoding 與實際不符指定 `reader.setEncoding("GBK")` 或輸出時 `format.setEncoding()`

最佳實踐清單

  1. 小文件用 DOM + XPath,代碼最簡潔。
  2. 大文件必須用 SAX/StAX,避免 OOM。
  3. 命名空間文件,**務必**註冊前綴再用 XPath。
  4. 寫入時用 `OutputFormat.createPrettyPrint()` 產生可讀 XML,生產可改 `createCompactFormat()` 減小體積。
  5. 解析外部不可信 XML 時,關閉 DTD/實體展開防止 XXE:`reader.setFeature("https://apache.org/xml/features/disallow-doctype-decl", true)`。

靈活切換 DOM/SAX/StAX 三種模式,配合 XPath 強大查詢,讓 DOM4J 成為 Java XML 處理的瑞士刀。

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