前言
XPath (XML Path Language) 是在 XML 文檔中查找信息的語言,提供了強大的節點選擇、條件過濾、軸導航等能力。本文系統整理 XPath 核心語法、七大節點類型、常用運算符、函數庫,並結合 Java javax.xml.xpath 包給出完整實戰代碼,涵蓋命名空間處理、自定義函數/變量解析器等進階主題。
一、XPath 七大節點類型
| 類型 | 說明 |
| 元素節點 | XML 元素,如 <book> |
| 屬性節點 | 元素屬性,如 lang="eng" |
| 文本節點 | 元素內的文本內容 |
| 命名空間節點 | XMLNS 宣告 |
| 處理指令節點 | 格式指令 |
| 註釋節點 | <!-- comment --> |
| 文檔節點(根節點) | 整個 XML 文檔的頂層容器 |
二、核心路徑表達式速查表
| 表達式 | 含義 | 示例結果 |
nodename | 選擇當前節點的所有子節點 | bookstore 選所有 bookstore 子節點 |
/ | 從根節點選擇 | /bookstore 選根元素 bookstore |
// | 從任意位置選擇匹配節點 | //book 選文檔中所有 book |
. | 選擇當前節點 | - |
.. | 選擇父節點 | - |
@ | 選擇屬性 | //@lang 選所有 lang 屬性 |
三、謂語 — 精準過濾節點
用方括號 [] 包裹條件:
/bookstore/book[1] — 第一個 book
/bookstore/book[last()] — 最後一個 book
//title[@lang='eng'] — lang="eng" 的 title
/bookstore/book[price>35.00]/title — 價格>35 的 book 的 title
四、通配符與多路徑選擇
| 通配符 | 說明 |
* | 匹配任意元素節點 |
@* | 匹配任意屬性節點 |
node() | 匹配任意類型節點 |
| | 合併多個路徑結果 |
示例://book/title | //book/price 同時選取所有 title 和 price。
五、軸 — 樹關係導航
語法:axis::node-test[predicate]。常用軸:
child::book — 子元素 book
attribute::lang — lang 屬性
descendant::book — 所有後代 book
ancestor::book — 所有祖先 book
following::* — 文檔中當前節點結束標籤後的所有節點
preceding-sibling::* — 當前節點前的所有同級節點
六、運算符與函數
支持算術(+ - * div mod)、比較(= != < <= > >=)、邏輯(and or)、節點集合併(|)。內置函數如 count()、contains()、starts-with()、substring()、string-length() 等。
七、Java 中使用 XPath 完整實戰
1. 基礎 API 流程
// 1. 創建 XPath 對象
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
// 2. 編譯表達式
XPathExpression expr = xpath.compile("//name/text()");
// 3. 解析 XML 文檔
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(false); // 無命名空間時
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(new File("test.xml"));
// 4. 執行查詢並指定返回類型
NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
// 5. 遍歷結果
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());
}
2. XPath 數據類型與 Java 映射
| XPath 類型 | Java 類型 | XPathConstants 常量 |
| node-set | org.w3c.dom.NodeList | NODESET |
| number | java.lang.Double | NUMBER |
| boolean | java.lang.Boolean | BOOLEAN |
| string | java.lang.String | STRING |
若不指定返回類型,evaluate(obj) 默認返回 String。
3. 命名空間處理 — 關鍵難點
XML 帶命名空間時,XPath 必須通過 NamespaceContext 註冊前綴:
// 自定義 NamespaceContext
class CustomNamespaceContext implements NamespaceContext {
public String getNamespaceURI(String prefix) {
if ("ns".equals(prefix)) return "https://www.example.com/ns";
if ("tg".equals(prefix)) return "https://www.example.com/tg";
if ("df".equals(prefix)) return "https://www.example.com/default";
return XMLConstants.NULL_NS_URI;
}
public String getPrefix(String uri) { return null; }
public Iterator getPrefixes(String uri) { return null; }
}
// 使用
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(new CustomNamespaceContext());
XPathExpression expr = xpath.compile("//df:computer"); // 使用註冊前綴
NodeList list = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
注意:即使元素使用默認命名空間(無前綴),查詢時也必須為其分配一個前綴(如 df),否則無法匹配。
4. 進階:自定義函數解析器與變量解析器
通過實現 XPathFunctionResolver 和 XPathVariableResolver 接口,可在 XPath 表達式中調用 Java 方法或使用外部變量:
xpath.setXPathFunctionResolver(new XPathFunctionResolver() {
public XPathFunction resolveFunction(QName name, int arity) {
if ("my:custom-func".equals(name.getLocalPart())) {
return args -> { /* 自定義邏輯 */ return "result"; };
}
return null;
}
});
xpath.setXPathVariableResolver(new XPathVariableResolver() {
public Object resolveVariable(QName name) {
if ("myVar".equals(name.getLocalPart())) return "external-value";
return null;
}
});
八、完整示例代碼
import javax.xml.parsers.*;
import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
public class XpathCompleteDemo {
public static void main(String[] args) throws Exception {
// 解析 XML
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true); // 必須開啟以支持命名空間
Document doc = factory.newDocumentBuilder().parse(new File("books.xml"));
XPathFactory xf = XPathFactory.newInstance();
XPath xpath = xf.newXPath();
// 1. 簡單查詢
NodeList titles = (NodeList) xpath.evaluate("//book/title/text()", doc, XPathConstants.NODESET);
for (int i = 0; i < titles.getLength(); i++) {
System.out.println("Title: " + titles.item(i).getNodeValue());
}
// 2. 條件查詢
Double price = (Double) xpath.evaluate("//book[price>35]/price", doc, XPathConstants.NUMBER);
System.out.println("First price > 35: " + price);
// 3. 命名空間查詢(需配合 NamespaceContext)
// xpath.setNamespaceContext(new CustomNamespaceContext());
// NodeList nsNodes = (NodeList) xpath.evaluate("//ns:book", doc, XPathConstants.NODESET);
}
}
總結
XPath 是 XML 處理的瑞士軍刀,掌握其路徑表達式、軸導航、謂語過濾、命名空間處理,配合 Java 標準 API,能高效完成複雜 XML 查詢與轉換任務。實戰中重點注意:開啟 NamespaceAware、正確註冊命名空間前綴、指定精確返回類型,避免類型轉換異常。
- 上一篇文章: 深入理解 Java final 變量的初始化規則與最佳實踐
- 下一篇文章: Java 比較器深度解析:Comparable vs Comparator 選型指南
Copyright ©
程式碼之家 All Rights Reserved