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

XML 程式設計總結 (七):XPath 查詢完全指南 - javax.xml.xpath 標準 API 實戰

     編輯:Java 綜合教學

XML 程式設計總結 (七):XPath 查詢完全指南 - javax.xml.xpath 標準 API 實戰

XPath 是專為 XML 文檔設計的查詢語言,Java 5+ 內建 javax.xml.xpath 包提供引擎無關的標準 API。本文詳細講解五種回傳類型、命名空間上下文、編譯緩存、異常處理與效能優化。

一、核心 API 體系

  • XPathFactory:工廠,newInstance() 取得預設實作 (Sun/Oracle 內建 XPath 1.0 引擎)。
  • XPath:編譯表達式、執行查詢、設定命名空間上下文。
  • XPathExpression:預編譯表達式,線程安全,可重複執行。
  • XPathConstants:五種回傳類型常量。

二、五種回傳類型與適用場景

常量Java 類型適用 XPath 結果典型用法
NODESETNodeList多節點集合//book, //title/text()
NODENode (單節點)單一元素/屬性/bookstore/book[1], //@id
STRINGString字串值 (自動串聯多節點文字)string(//book[1]/title)
NUMBERDouble數值 (自動轉換)count(//book), sum(//price)
BOOLEANBoolean真/假boolean(//book[@category='WEB'])

三、完整實戰代碼範本

import javax.xml.xpath.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;

public class XPathDemo {
    public static void main(String[] args) throws Exception {
        // 1. 載入 XML 文檔到 DOM 樹
        DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = builder.parse(new File("books.xml"));

        // 2. 建立 XPath 物件
        XPathFactory factory = XPathFactory.newInstance();
        XPath xpath = factory.newXPath();

        // 3. 註冊命名空間 (若 XML 有命名空間,必須實作 NamespaceContext)
        // xpath.setNamespaceContext(new MyNamespaceContext());

        // 4. 編譯表達式 (重用 XPathExpression)
        XPathExpression exprBook = xpath.compile("//book[@category='WEB']");
        XPathExpression exprTitle = xpath.compile("title/text()");
        XPathExpression exprCount = xpath.compile("count(//book)");

        // 5. 執行查詢 - 示範五種回傳類型

        // NODESET -> NodeList
        NodeList books = (NodeList) exprBook.evaluate(doc, XPathConstants.NODESET);
        System.out.println("WEB 類書籍數: " + books.getLength());
        for (int i = 0; i < books.getLength(); i++) {
            Element book = (Element) books.item(i);
            // 相對當前節點查詢
            String title = (String) exprTitle.evaluate(book, XPathConstants.STRING);
            System.out.println("  - " + title);
        }

        // NODE -> 單一元素
        Element firstBook = (Element) xpath.compile("/bookstore/book[1]")
            .evaluate(doc, XPathConstants.NODE);
        System.out.println("第一本書: " + firstBook.getElementsByTagName("title").item(0).getTextContent());

        // STRING -> 直接取文字
        String price = (String) xpath.compile("string(//book[@category='COOKING']/price)")
            .evaluate(doc, XPathConstants.STRING);
        System.out.println("烹飲類價格: " + price);

        // NUMBER -> 計數/求和
        Double count = (Double) exprCount.evaluate(doc, XPathConstants.NUMBER);
        Double sum = (Double) xpath.compile("sum(//price)").evaluate(doc, XPathConstants.NUMBER);
        System.out.println("總冊數: " + count.intValue() + ", 總價: " + sum);

        // BOOLEAN -> 存在性判斷
        Boolean hasJava = (Boolean) xpath.compile("boolean(//book[contains(title,'Java')])")
            .evaluate(doc, XPathConstants.BOOLEAN);
        System.out.println("是否有 Java 相關書: " + hasJava);
    }
}

四、命名空間上下文實作 (關鍵難點)

XML 若宣告 xmlns="..." 或 xmlns:ns="...",XPath 必須 註冊前綴映射,否則查無結果。

import javax.xml.namespace.NamespaceContext;
import java.util.*;

public class SimpleNamespaceContext implements NamespaceContext {
    private final Map<String, String> nsMap;

    public SimpleNamespaceContext(Map<String, String> nsMap) { this.nsMap = nsMap; }

    @Override public String getNamespaceURI(String prefix) { return nsMap.get(prefix); }
    @Override public String getPrefix(String uri) { /* 反向查找略 */ return null; }
    @Override public Iterator<String> getPrefixes(String uri) { return Collections.emptyIterator(); }
}

// 使用
Map<String, String> ns = new HashMap<>();
ns.put("bk", "https://example.com/bookstore");
xpath.setNamespaceContext(new SimpleNamespaceContext(ns));
// 現在可用 //bk:book 查詢

五、效能優化與最佳實踐

  1. 重用 XPathExpression:compile() 開銷較大,靜態編譯一次、多次 evaluate()。
  2. 避免在循環中重複編譯:將表達式提取為常量或成員變量。
  3. 大文檔改用 StAX + XPath:DOM 樹記憶體壓力大時,可結合 XMLStreamReader 逐塊處理 (需第三方庫如 Saxon 支援流式 XPath)。
  4. 關閉 DTD/實體解析:DocumentBuilderFactory 設定 setFeature("https://apache.org/xml/features/disallow-doctype-decl", true) 防 XXE 攻擊。
  5. 型別安全包裝:封裝 evaluateAsString(), evaluateAsNodeList() 等輔助方法,避免到處強轉。

六、常見異常與排查

  • XPathExpressionException:語法錯誤、命名空間未註冊、表達式對當前文檔無效。
  • NullPointerException:evaluate() 回傳 null 時強轉 (如 NODE 無匹配)。請先用 BOOLEAN 判斷存在性。
  • ClassCastException:請求類型與實際結果不符 (如 NODESET 強轉 Node)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved