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

dom4j 實戰全書:XML 創建、解析、修改、XPath 查詢與命名空間處理

     編輯:Java 綜合教學

為什麼選 dom4j?

  • API 直觀:鏈式調用、類 DOM 但更 Java 化。
  • 效能優異:SAX 解析底層、記憶體佔用低於標準 DOM。
  • 功能強大:原生 XPath、XSLT、命名空間、事件流式處理。
  • 生態成熟:Hibernate、Spring、眾多開源項目長期依賴。

Maven 坐標:org.dom4j:dom4j:2.1.4 (需額外引入 jaxen:jaxen:1.2.0 支援 XPath)。

核心類速覽 (org.dom4j)

介面/類職責
DocumentXML 文檔根節點
Element元素節點,可含屬性、子元素、文本
Attribute屬性節點
Node所有節點父介面
SAXReaderSAX 解析器,生成 Document
XMLWriter配合 OutputFormat 輸出格式化 XML
DocumentHelper工廠方法創建 Document/Element

場景一:創建 XML 文件

public void createXml(String filePath) throws IOException {
    Document doc = DocumentHelper.createDocument();
    Element root = doc.addElement("students");

    Element stu1 = root.addElement("student")
        .addAttribute("id", "s001")
        .addAttribute("enrolled", "2023-09-01");
    stu1.addElement("name").setText("劉光藍");
    stu1.addElement("gender").setText("男");
    stu1.addElement("age").setText("25");
    stu1.addElement("courses")
        .addElement("course").addAttribute("code", "CS101").setText("資料結構")
        .addElement("course").addAttribute("code", "CS102").setText("演算法");

    OutputFormat fmt = OutputFormat.createPrettyPrint();
    fmt.setEncoding("UTF-8");
    fmt.setIndent("  ");
    fmt.setNewlines(true);

    try (FileWriter fw = new FileWriter(filePath);
         XMLWriter writer = new XMLWriter(fw, fmt)) {
        writer.write(doc);
    }
}

輸出:

<?xml version="1.0" encoding="UTF-8"?>
<students>
  <student id="s001" enrolled="2023-09-01">
    <name>劉光藍</name>
    <gender>男</gender>
    <age>25</age>
    <courses>
      <course code="CS101">資料結構</course>
      <course code="CS102">演算法</course>
    </courses>
  </student>
</students>

場景二:SAXReader 解析 + XPath 查詢

public void parseXml(String filePath) throws Exception {
    SAXReader reader = new SAXReader();
    Document doc = reader.read(new File(filePath));

    // 1. 選取所有 student 元素
    List<Node> students = doc.selectNodes("//student");
    for (Node node : students) {
        Element stu = (Element) node;
        System.out.println("ID: " + stu.attributeValue("id"));
        System.out.println("Name: " + stu.elementText("name"));
        System.out.println("Age: " + stu.elementText("age"));
    }

    // 2. 條件查詢:id 為 s001 的學生
    Element target = (Element) doc.selectSingleNode("//student[@id='s001']");
    if (target != null) System.out.println("Found: " + target.elementText("name"));

    // 3. 遍歷課程
    List<Node> courses = doc.selectNodes("//student[@id='s001']/courses/course");
    for (Node n : courses) {
        Element c = (Element) n;
        System.out.println("Course: " + c.attributeValue("code") + " = " + c.getText());
    }
}

場景三:修改 XML (更新屬性、增刪節點)

public void updateXml(String filePath) throws Exception {
    SAXReader reader = new SAXReader();
    Document doc = reader.read(new File(filePath));

    // 修改屬性
    Element stu = (Element) doc.selectSingleNode("//student[@id='s001']");
    stu.addAttribute("id", "stu001"); // 覆蓋舊屬性

    // 新增子元素
    stu.addElement("email").setText("liu@example.com");

    // 刪除節點
    Element gender = stu.element("gender");
    if (gender != null) gender.detach(); // 從父節點移除

    // 寫回文件
    OutputFormat fmt = OutputFormat.createPrettyPrint();
    fmt.setEncoding("UTF-8");
    try (FileWriter fw = new FileWriter(filePath);
         XMLWriter writer = new XMLWriter(fw, fmt)) {
        writer.write(doc);
    }
}

場景四:命名空間處理 (關鍵難點)

XML 帶命名空間時,XPath 必須註冊前綴,否則查無結果:

<root xmlns:ns="https://example.com/ns">
  <ns:item id="1">Value</ns:item>
</root>
SAXReader reader = new SAXReader();
Document doc = reader.read(new File("ns.xml"));

// 註冊命名空間上下文
Map<String, String> nsMap = Map.of("ns", "https://example.com/ns");
org.jaxen.XPath xpath = doc.createXPath("//ns:item[@id='1']");
xpath.setNamespaceContext(new SimpleNamespaceContext(nsMap));

Element item = (Element) xpath.selectSingleNode(doc);
System.out.println(item.getText()); // Value

// SimpleNamespaceContext 實現
public class SimpleNamespaceContext implements org.jaxen.NamespaceContext {
    private final Map<String, String> map;
    public SimpleNamespaceContext(Map<String, String> map) { this.map = map; }
    @Override public String translateNamespacePrefixToUri(String prefix) { return map.get(prefix); }
    @Override public String getNamespacePrefix(String uri) { return map.entrySet().stream().filter(e -> e.getValue().equals(uri)).map(Map.Entry::getKey).findFirst().orElse(null); }
    @Override public Iterator<String> getPrefixes() { return map.keySet().iterator(); }
}

場景五:大文件流式處理 (避免 OOM)

dom4j 提供 SAXReader + ElementHandler 邊解析邊處理,不將整樹載入記憶體:

public void streamParse(String filePath) throws Exception {
    SAXReader reader = new SAXReader();
    reader.addHandler("/students/student", new ElementHandler() {
        @Override public void onStart(ElementPath path) {}
        @Override public void onEnd(ElementPath path) {
            Element stu = path.getCurrent();
            // 處理單個 student
            System.out.println("Processing: " + stu.attributeValue("id"));
            stu.detach(); // 釋放記憶體,關鍵!
        }
    });
    reader.read(new File(filePath)); // 觸發流式解析
}

常見坑與最佳實踐

  1. 編碼一致:讀寫均指定 UTF-8,避免中文亂碼。
  2. XPath 注入防範:拼接用戶輸入時轉義單引號,或用參數化 VariableContext。
  3. 釋放記憶體:流式處理時 detach() 已處理節點;普通解析完畢 doc = null 助 GC。
  4. 實體引用:預設不展開外部實體 (防 XXE),需解析 DTD 時 reader.setFeature("https://apache.org/xml/features/disallow-doctype-decl", false)。
  5. 替代方案:簡單配置用 Jackson XML / JAXB 綁定物件;高效能序列化用 Protobuf/FlatBuffers。

總結

dom4j 以 鏈式 API + 原生 XPath + 流式處理 三大殺手鐧,成為 Java XML 處理的瑞士刀。掌握「創建→解析→修改→命名空間→流式」五大場景,應對 95% 的 XML 業務需求綽綽有餘。

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