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

Java XML 處理全攻略:DOM、SAX、JDOM、DOM4J、XPath 深度對比與實戰

     編輯:Java 程式設計入門

前言:為什麼 2024 年還要學 XML?

雖然 JSON 已成為 API 交換主流,但 XML 在企業級配置、SOAP 服務、Office 文檔、Android 佈局、Maven POM、SVG 圖形等領域仍佔統治地位。Java 提供了豐富的 XML 處理生態,本文將系統梳理五大主流解析技術的原理、效能、適用場景,並提供生產級代碼範本。

五大解析技術核心對比

維度DOM (W3C)SAXJDOMDOM4JXPath (JAXP)
解析模式樹模型(全量載入記憶體)事件流模型(流式讀取)樹模型(Java 原生 API)樹模型(高效能實現)查詢語言(需配合 DOM/DOM4J)
記憶體佔用高(文檔大小 5-10 倍)極低(常數級)中高中(優化過的樹結構)取決於底層模型
查詢靈活性完整樹遍歷單向流式,無法回溯友好 API強大(XPath、XSLT、索引)聲明式路徑查詢
修改寫入原生支援不支援(需自行構建)支援支援(OutputFormat 格式化)僅查詢
學習曲線標準但冗長需理解事件回調低(Java 風格)低(流式 API)需學習 XPath 語法
典型適用場景小型配置、需隨機訪問修改大文件、單次順序讀取、內存受限中小項目、追求代碼簡潔企業級應用、複雜 XML 處理複雜條件查詢、數據提取

統一測試數據模型

所有示例針對同一 XML 結構:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <TelePhone>
    <type name="nokia">
      <price>599</price>
      <operator>CMCC</operator>
    </type>
    <type name="xiaomi">
      <price>699</price>
      <operator>ChinaNet</operator>
    </type>
  </TelePhone>
</root>

1. DOM (Document Object Model) —— 標準重型選手

核心特點

  • W3C 標準,org.w3c.dom 套件,JDK 內建無需額外依賴
  • 將整個文檔構建為樹狀結構,Node 為基礎類型,Element 為最常用子類
  • Element vs Node:Element 代表完整元素(含屬性、子節點),Node 為抽象基類(含 Text、Attribute、Comment 等)

生產級封裝類


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.*;

public class DomXmlHandler {

    /** 創建 XML 並輸出字串與文件 */
    public String createXml() throws ParserConfigurationException, TransformerException, IOException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.newDocument();
        doc.setXmlVersion("1.0");
        doc.setXmlStandalone(true);

        Element root = doc.createElement("root");
        doc.appendChild(root);

        Element telephone = doc.createElement("TelePhone");
        root.appendChild(telephone);

        addPhoneType(doc, telephone, "nokia", "599", "CMCC");
        addPhoneType(doc, telephone, "xiaomi", "699", "ChinaNet");

        // 輸出字串
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");

        StringWriter writer = new StringWriter();
        transformer.transform(new DOMSource(doc), new StreamResult(writer));

        // 寫入文件
        try (FileOutputStream fos = new FileOutputStream("TelePhone_DOM.xml")) {
            transformer.transform(new DOMSource(doc), new StreamResult(fos));
        }

        return writer.toString();
    }

    private void addPhoneType(Document doc, Element parent, String name, String price, String operator) {
        Element type = doc.createElement("type");
        type.setAttribute("name", name);

        Element priceEl = doc.createElement("price");
        priceEl.setTextContent(price);
        type.appendChild(priceEl);

        Element opEl = doc.createElement("operator");
        opEl.setTextContent(operator);
        type.appendChild(opEl);

        parent.appendChild(type);
    }

    /** 解析 XML 字串 */
    public void parseXml(String xml) throws ParserConfigurationException, SAXException, IOException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new InputSource(new StringReader(xml)));

        Element root = doc.getDocumentElement();
        NodeList types = root.getElementsByTagName("type");

        for (int i = 0; i < types.getLength(); i++) {
            Element type = (Element) types.item(i);
            String name = type.getAttribute("name");
            String price = getChildText(type, "price");
            String operator = getChildText(type, "operator");
            System.out.printf("Phone: %s, Price: %s, Operator: %s%n", name, price, operator);
        }
    }

    private String getChildText(Element parent, String tagName) {
        NodeList list = parent.getElementsByTagName(tagName);
        return list.getLength() > 0 ? list.item(0).getTextContent() : "";
    }
}

2. DOM4J —— 企業級首選(Hibernate、Spring 早期採用)

核心優勢

  • 原生支援 XPath、XSLT、XML Schema
  • 流式寫入大文件(XMLWriter + OutputFormat)
  • API 極度符合 Java 習慣(鏈式調用、泛型集合)

依賴:dom4j + jaxen(XPath 引擎,缺失會報 NoClassDefFoundError: org/jaxen/JaxenException)

完整實現


import org.dom4j.*;
import org.dom4j.io.*;
import java.io.*;
import java.util.List;

public class Dom4jXmlHandler {

    public String createXml() throws IOException {
        Document doc = DocumentHelper.createDocument();
        Element root = doc.addElement("root");
        Element telephone = root.addElement("TelePhone");

        addPhone(telephone, "nokia", "599", "CMCC");
        addPhone(telephone, "xiaomi", "699", "ChinaNet");

        // 格式化輸出字串
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("UTF-8");
        StringWriter writer = new StringWriter();
        XMLWriter xmlWriter = new XMLWriter(writer, format);
        xmlWriter.write(doc);
        xmlWriter.close();

        // 寫入文件
        try (FileWriter fw = new FileWriter("TelePhone_DOM4J.xml")) {
            XMLWriter fileWriter = new XMLWriter(fw, format);
            fileWriter.write(doc);
        }

        return writer.toString();
    }

    private void addPhone(Element parent, String name, String price, String operator) {
        Element type = parent.addElement("type").addAttribute("name", name);
        type.addElement("price").addText(price);
        type.addElement("operator").addText(operator);
    }

    /** 標準遍歷解析 */
    public void parseXml(String xml) throws DocumentException {
        SAXReader reader = new SAXReader();
        Document doc = reader.read(new InputSource(new StringReader(xml)));

        Element root = doc.getRootElement();
        List types = root.element("TelePhone").elements("type");

        for (Element type : types) {
            String name = type.attributeValue("name");
            List children = type.elements();
            for (Element child : children) {
                System.out.printf("%s: %s=%s%n", name, child.getName(), child.getText());
            }
        }
    }

    /** XPath 查詢(推薦複雜條件) */
    public void parseByXPath(String xml) throws DocumentException {
        SAXReader reader = new SAXReader();
        Document doc = reader.read(new InputSource(new StringReader(xml)));

        List types = doc.selectNodes("/root/TelePhone/type");
        for (Element type : types) {
            String name = type.attributeValue("name");
            List props = type.selectNodes("./*");
            for (Element prop : props) {
                System.out.printf("%s: %s=%s%n", name, prop.getName(), prop.getText());
            }
        }
    }
}

3. SAX —— 大文件流式處理王者

SAX 不建樹,通過 ContentHandler 回調事件(startElement、characters、endElement)。適合:

  • GB 級 XML 文件
  • 只需提取特定標籤,無需隨機訪問
  • 內存極度受限環境(嵌入式、舊版 Android)

代碼較冗長,此處略過,核心在於維護狀態機(當前路徑、臨時數據)。

4. JAXP XPath —— 標準查詢引擎

JDK 1.5+ 內建 javax.xml.xpath,可作用於任何 Document(DOM、DOM4J 轉換後)。


public class JaxpXPathHandler {

    public void query(String xml) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(false); // 無命名空間
        Document doc = factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));

        XPathFactory xpf = XPathFactory.newInstance();
        XPath xpath = xpf.newXPath();

        // 編譯表達式(可重用)
        XPathExpression expr = xpath.compile("/root/TelePhone/type");
        NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);

        for (int i = 0; i < nodes.getLength(); i++) {
            Element type = (Element) nodes.item(i);
            String name = type.getAttribute("name");
            System.out.println("Phone: " + name);

            XPathExpression childExpr = xpath.compile("./*");
            NodeList children = (NodeList) childExpr.evaluate(type, XPathConstants.NODESET);
            for (int j = 0; j < children.getLength(); j++) {
                Element child = (Element) children.item(j);
                System.out.printf("  %s = %s%n", child.getTagName(), child.getTextContent());
            }
        }
    }
}

5. 命名空間處理:XPath 的隱形殺手

真實項目中 XML 常含命名空間:


<tg:bookstore xmlns:tg="https://example.com/tg" xmlns:ns="https://example.com/ns">
  <ns:book><tg:title>Hello</tg:title></ns:book>
</tg:bookstore>

XPath 查詢必須註冊前綴映射,否則 //book 匹配不到 ns:book。


public class NamespaceContextImpl implements NamespaceContext {
    private final Map map = Map.of(
        "df", "https://example.com/tg",   // default namespace 映射為 df
        "ns", "https://example.com/ns",
        "tg", "https://example.com/tg"
    );

    @Override public String getNamespaceURI(String prefix) { return map.getOrDefault(prefix, XMLConstants.NULL_NS_URI); }
    @Override public String getPrefix(String uri) { return null; }
    @Override public Iterator getPrefixes(String uri) { return null; }
}

// 使用
xpath.setNamespaceContext(new NamespaceContextImpl());
XPathExpression expr = xpath.compile("//df:book/ns:title"); // 必須帶前綴

效能基準測試參考(10MB XML,10 萬節點)

操作DOMSAXDOM4JJAXP XPath (DOM)
解析時間~450ms~180ms~220ms~480ms
堆內存峰值~180MB~5MB~90MB~180MB
XPath 查詢 1000 次N/AN/A~15ms~25ms

選型決策樹

需修改寫入?
  ├─ 是 → 文件 < 50MB?→ DOM / DOM4J
  │           └─ 文件 > 50MB?→ StAX (JDK 內建流式寫入) 或分片處理
  └─ 否 → 僅查詢/讀取?
            ├─ 複雜條件、多次查詢 → DOM4J + XPath
            ├─ 單次順序讀取、大文件 → SAX / StAX
            └─ 簡單提取、標準無依賴 → JAXP XPath (DOM)

現代替代方案:Jackson XML / JAXB

  • JAXB (Java Architecture for XML Binding):Java 6-10 內建,Java 11+ 需單獨引入。適合「XML ⇄ Java Object」雙向映射,註解驅動,極大減少樣板代碼。
  • Jackson XML Module:com.fasterxml.jackson.dataformat:jackson-dataformat-xml,複用 JSON 註解,效能優於 JAXB,Spring Boot 2.x+ 默認整合。

新項目建議優先考慮 JAXB/Jackson,僅在需動態結構、非強類型場景下使用 DOM4J/XPath。

總結

Java XML 處理已形成成熟體系:

  • DOM:標準、通用、小文件修改首選
  • SAX/StAX:大文件、流式、低內存
  • DOM4J:功能最全、XPath 最強、企業級首選
  • JAXP XPath:標準查詢 API,配合任意 DOM 實現
  • JAXB/Jackson:現代綁定框架,業務開發首選

掌握各技術記憶體模型、線程安全性、命名空間處理、異常捕獲細節,才能在生產環境游刃有餘。

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