# Java XPath 示例 – XPath 教程

> 原文： [https://howtodoinjava.com/xml/java-xpath-tutorial-example/](https://howtodoinjava.com/xml/java-xpath-tutorial-example/)

在此 **Java XPath 教程**中，我们将学习什么是 **XPath 库**，什么是 XPath 数据类型，并学习创建 XPath 表达式语法以从 XML 文件或文档中检索信息。 此信息可以是 XML 节点或 XML 属性，甚至可以是注释。

```java
Table of Contents

1\. What is XPath?
2\. XPath Data Model
3\. XPath Data Types
4\. XPath Syntax
5\. XPath Expressions
6\. Recommended reading
```

在本教程中，我们将在运行各种 **XPath 示例**时使用此 XML。

```java
<?xml version="1.0" encoding="utf-8" ?>
<inventory>
	<!--Test is test comment-->
		<book year="2000">
		<title>Snow Crash</title>
		<author>Neal Stephenson</author>
		<publisher>Spectra</publisher>
		<isbn>0553380958</isbn>
		<price>14.95</price>
	</book>
	<book year="2005">
		<title>Burning Tower</title>
		<author>Larry Niven</author>
		<author>Jerry Pournelle</author>
		<publisher>Pocket</publisher>
		<isbn>0743416910</isbn>
		<price>5.99</price>
	</book>
	<book year="1995">
		<title>Zodiac</title>
		<author>Neal Stephenson</author>
		<publisher>Spectra</publisher>
		<isbn>0553573862</isbn>
		<price>7.50</price>
	</book>
</inventory>

```

## 1.什么是 XPath

[**XPath**](https://en.wikipedia.org/wiki/XPath "XPath") 是一种语法，用于描述 [XML](https://en.wikipedia.org/wiki/XML "XML") 文档的各个部分。 使用 XPath，您可以引用第一个元素，元素的任何属性，包含某些文本的所有特定元素以及许多其他变体。 [XSLT](https://en.wikipedia.org/wiki/XSLT "XSLT") 样式表在匹配项中使用 XPath 表达式，并选择各种元素的属性来指示应如何转换文档。

在使用 XML 发送请求和接收响应来测试 [Web 服务](https://en.wikipedia.org/wiki/Web_service "Web services")时，XPath 有时会很有用。

XPath 使用的语言语法与我们已经知道的非常相似。 语法是基本编程语言表达式（例如`$x*6`的通配符）和**类 Unix 路径表达式**（例如`/inventory/author`）的混合。

除了基本语法外，XPath 还提供了一组有用的函数（例如`count()`或`contains()`，与工具函数调用非常相似），这些函数使您可以搜索文档中的各种数据片段。

## 2\. XPath 数据模型

XPath 将 XML 文档视为节点的[树](https://en.wikipedia.org/wiki/Tree_%28data_structure%29 "tree")。 该树与[文档对象模型](https://www.w3.org/DOM/ "DOM")（即 DOM 树）非常相似，因此，如果您熟悉 DOM，则可以轻松了解如何构建基本的 **XPath 表达式**。

XPath 数据模型中有七种节点：

1.  根节点（每个文档仅一个）
2.  元素节点
3.  属性节点
4.  文本节点
5.  注释节点
6.  处理指令节点
7.  命名空间节点

#### 2.1 根节点

[根节点](https://en.wikipedia.org/wiki/Root_element "root node")是包含整个文档的 XPath 节点。 在我们的示例中，根节点包含`<inventory>`元素。 在 XPath 表达式中，用单斜杠（`'/'`）指定根节点。

#### 2.2 元素节点

原始 XML 文档中的每个元素都由一个 XPath 元素节点表示。

例如，在下面的示例 XML 中，是元素节点。

*   `book`
*   `title`
*   `author`
*   `publisher`
*   `isbn`
*   `price`

#### 2.3 属性节点

元素节点至少是 XML 源文档中每个属性的一个属性节点的父级。 **这些节点用于定义有关特定元素节点**的特征。

例如，在我们的 XML 片段“`year`”中是一个属性节点。

#### 2.4 文本节点

文本节点非常简单。 **它们包含来自元素**的文本。 如果 XML 文档中的原始文本包含实体或字符引用，则在创建 XPath 文本节点之前将其解析。

文本节点是纯净的文本。 文本节点需要包含尽可能多的文本。 *请记住，文本节点的下一个或上一个节点不能是另一个文本节点。*

例如，我们 XML 片段中的所有值都是文本节点，例如 “`Snow Crash`”和“`Neal Stephenson`”。

#### 2.5 注释节点

注释节点也非常简单 - 它包含一些文本。 **源文档中的每个注释都将成为注释节点**。 注释节点的文本包含注释中的所有内容，除了`<!--`开头和`-->`结尾。

例如：

```java
<!--Test is test comment-->
```

#### 2.6 处理指令节点

**处理指令节点具有两个部分**，**名称**（由`name()`函数返回）和**字符串值**。 字符串值是名称`<?xml`之后的所有内容，包括空格，但不包括关闭处理指令的`?>`。

例如：

```java
<?xml version="1.0" encoding="utf-8"?>
```

#### 2.7 命名空间节点

XSLT 样式表几乎从未使用过命名空间节点。 **它们的存在主要是为了 XSLT 处理器的利益**。

> 请记住，即使从技术上来说，名称空间的声明（例如 `xmlns:auth="http://www.authors.net"`）在 XML 源中仍是一个属性，但它成为名称空间节点，而不是属性节点。

## 3\. XPath 数据类型

在 Java 中，XPath 表达式可能返回以下数据类型之一：

1.  [**节点集**](https://docs.oracle.com/javase/1.5.0/docs/api/javax/xml/xpath/XPathConstants.html#NODESET "NODESET") – 表示一组节点。 该集合可以为空，也可以包含任意数量的节点。
2.  [**节点**](https://docs.oracle.com/javase/1.5.0/docs/api/javax/xml/xpath/XPathConstants.html#NODE "NODE") （Java 支持）– 表示单个节点。 它可以为空，也可以包含任意数量的子节点。
3.  [**布尔值**](https://docs.oracle.com/javase/1.5.0/docs/api/javax/xml/xpath/XPathConstants.html#BOOLEAN "BOOLEAN") – 表示值`true`或`false`。 请注意，在 XPath 中，`true`或`false`字符串没有特殊含义或值。 有关布尔值的更详细讨论，请参见第 4 章的 4.2.1.2 节。
4.  [**数字**](https://docs.oracle.com/javase/1.5.0/docs/api/javax/xml/xpath/XPathConstants.html#NUMBER "NUMBER") – 表示浮点数。 XPath 和 XSLT 中的所有数字均实现为浮点数； XPath 和 XSLT 中不存在整数（或`int`）数据类型。 具体来说，所有数字都实现为 IEEE 754 浮点数，与 Java `float`和`double`原始类型使用的标准相同。 除普通数字外，数字还有五个特殊值：正负无穷大，正负零和`NaN`（非数字的特殊符号）。
5.  [**字符串**](https://docs.oracle.com/javase/1.5.0/docs/api/javax/xml/xpath/XPathConstants.html#STRING "STRING") – 表示零个或多个字符，如 XML 规范中所定义。

这些数据类型通常很简单，并且除了节点集之外，类型之间的转换通常很简单。 我们将不在这里更详细地讨论这些数据类型。 相反，我们将讨论数据类型和转换，因为我们需要它们来执行特定的任务。

## 4\. XPath 语法

XPath 使用 UNIX 和[正则表达式](https://en.wikipedia.org/wiki/Regular_expression "Regular expression")这种语法。

#### 4.1 选择具有 xpath 的节点

| 表达式 | 描述 |
| --- | --- |
| `nodename` | 选择所有名称为“`nodename`”的节点 |
| `/` | 从根节点选择 |
| `//` | 从当前节点中选择匹配选择的节点，无论它们在何处 |
| `.` | 选择当前节点 |
| `..` | 选择当前节点的父节点 |
| `@` | 选择属性 |

#### 4.2 将谓词与 xpath 一起使用

**谓词用于查找特定节点或包含特定值的节点。** *谓词始终嵌入在方括号中。*
我们将在下一节中学习如何使用它们。

#### 4.3 使用 xpath 到达未知节点

**XPath 通配符**可用于选择未知的 XML 元素。

| 通配符 | 描述 |
| --- | --- |
| `*` | 匹配任何元素节点 |
| `@*` | 匹配任何属性节点 |
| `node()` | 匹配任何种类的任何节点 |

#### 4.4 XPath 轴

[轴](https://www.w3schools.com/xsl/xpath_axes.asp "xpath axis")定义相对于当前节点的节点集。 以下是默认定义的轴。

| 轴名 | 结果 |
| --- | --- |
| `ancestor` | 选择当前节点的所有祖先（父，祖父等） |
| `ancestor-or-self` | 选择当前节点的所有祖先（父，祖父等）和当前节点本身 |
| `attribute` | 选择当前节点的所有属性 |
| `child` | 选择当前节点的所有子节点 |
| `descendant` | 选择当前节点的所有后代（子代，孙代等） |
| `descendant-or-self` | 选择当前节点的所有后代（子代，孙代等）和当前节点本身 |
| `following` | 选择当前节点的结束标记之后的文档中的所有内容 |
| `following-sibling` | 选择当前节点之后的所有同级 |
| `namespace` | 选择当前节点的所有名称空间节点 |
| `parent` | 选择当前节点的父节点 |
| `preceding` | 选择出现在文档中当前节点之前的所有节点，但祖先，属性节点和名称空间节点除外 |
| `preceding-sibling` | 选择当前节点之前的所有同级 |
| `self` | 选择当前节点 |

#### 4.5 XPath 运算符

以下是可在 XPath 表达式中使用的 **xpath 运算符**的列表：


| 运算符 | 描述 | 例 | 返回值 |
| --- | --- | --- | --- |
| <code>&#124;</code> | 计算两个节点集 | <code>//book &#124; //cd</code> | 返回包含所有`book`和`cd`元素的节点集 |
| `+` | 加法 | `6 + 4` | 10 |
| `-` | 减法 | `6 – 4` | 2 |
| `*` | 乘法 | `6 * 4` | 24 |
| `div` | 除法 | `8 div 4` | 2 |
| `=` | 等于 | `price = 9.80` | 如果价格为 9.80，则为`true`，如果价格为 9.90，则为`false` |
| `!=` | 不等于 | `price != 9.80` | 如果价格为 9.90，则为`true`，如果价格为 9.80，则为`false` |
| `<` | 小于 | `price < 9.80` | 如果价格为 9.00，则为`true`，如果价格为 9.80，则为`false` |
| `< =` | 小于或等于 | `price <= 9.80` | 如果价格为 9.00，则为`true`，如果价格为 9.90，则为`false` |
| `>` | 大于 | `prcie > 9.80` | 如果价格为 9.90，则为`true`，如果价格为 9.80，则为`false` |
| `>=` | 大于或等于 | `price >= 9.80` | 如果价格为 9.90，则为`true`，如果价格为 9.70，则为`false` |
| `or` | 或 | `price = 9.80 or price = 9.70` | 如果价格为 9.80，则为`true`，如果价格为 9.50，则为`false` |
| `and` | 且 | `price > 9.00 and price < 9.90`  | 如果价格为 9.80，则为`true`，如果价格为 8.50，则为`false` |
| `mod` | 模数（除法余数） | `5 mod 2` | 1 |

## 5\. XPath 表达式

让我们尝试使用 XPath 表达式和给定的数据类型来检索 XML 的不同部分。

```java
package xml;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

public class XPathTest 
{
	public static void main(String[] args) throws Exception 
	{
		//Build DOM

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true); // never forget this!
		DocumentBuilder builder = factory.newDocumentBuilder();
		Document doc = builder.parse("inventory.xml");

		//Create XPath

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

		System.out.println("n//1) Get book titles written after 2001");

		// 1) Get book titles written after 2001
		XPathExpression expr = xpath.compile("//book[@year>2001]/title/text()");
		Object result = expr.evaluate(doc, XPathConstants.NODESET);
		NodeList nodes = (NodeList) result;
		for (int i = 0; i < nodes.getLength(); i++) {
			System.out.println(nodes.item(i).getNodeValue());
		}

		System.out.println("n//2) Get book titles written before 2001");

		// 2) Get book titles written before 2001
		expr = xpath.compile("//book[@year<2001]/title/text()");
		result = expr.evaluate(doc, XPathConstants.NODESET);
		nodes = (NodeList) result;
		for (int i = 0; i < nodes.getLength(); i++) {
			System.out.println(nodes.item(i).getNodeValue());
		}

		System.out.println("n//3) Get book titles cheaper than 8 dollars");

		// 3) Get book titles cheaper than 8 dollars
		expr = xpath.compile("//book[price<8]/title/text()");
		result = expr.evaluate(doc, XPathConstants.NODESET);
		nodes = (NodeList) result;
		for (int i = 0; i < nodes.getLength(); i++) {
			System.out.println(nodes.item(i).getNodeValue());
		}

		System.out.println("n//4) Get book titles costlier than 8 dollars");

		// 4) Get book titles costlier than 8 dollars
		expr = xpath.compile("//book[price>8]/title/text()");
		result = expr.evaluate(doc, XPathConstants.NODESET);
		nodes = (NodeList) result;
		for (int i = 0; i < nodes.getLength(); i++) {
			System.out.println(nodes.item(i).getNodeValue());
		}

		System.out.println("n//5) Get book titles added in first node");

		// 5) Get book titles added in first node
		expr = xpath.compile("//book[1]/title/text()");
		result = expr.evaluate(doc, XPathConstants.NODESET);
		nodes = (NodeList) result;
		for (int i = 0; i < nodes.getLength(); i++) {
			System.out.println(nodes.item(i).getNodeValue());
		}

		System.out.println("n//6) Get book title added in last node");

		// 6) Get book title added in last node
		expr = xpath.compile("//book[last()]/title/text()");
		result = expr.evaluate(doc, XPathConstants.NODESET);
		nodes = (NodeList) result;
		for (int i = 0; i < nodes.getLength(); i++) {
			System.out.println(nodes.item(i).getNodeValue());
		}

		System.out.println("n//7) Get all writers");

		// 7) Get all writers
		expr = xpath.compile("//book/author/text()");
		result = expr.evaluate(doc, XPathConstants.NODESET);
		nodes = (NodeList) result;
		for (int i = 0; i < nodes.getLength(); i++) {
			System.out.println(nodes.item(i).getNodeValue());
		}

		System.out.println("n//8) Count all books titles ");

		// 8) Count all books titles
		expr = xpath.compile("count(//book/title)");
		result = expr.evaluate(doc, XPathConstants.NUMBER);
		Double count = (Double) result;
		System.out.println(count.intValue());

		System.out.println("n//9) Get book titles with writer name start with Neal");

		// 9) Get book titles with writer name start with Neal
		expr = xpath.compile("//book[starts-with(author,'Neal')]");
		result = expr.evaluate(doc, XPathConstants.NODESET);
		nodes = (NodeList) result;
		for (int i = 0; i < nodes.getLength(); i++) {
			System.out.println(nodes.item(i)
								.getChildNodes()
								.item(1)				//node <title> is on first index
								.getTextContent());
		}

		System.out.println("n//10) Get book titles with writer name containing Niven");

		// 10) Get book titles with writer name containing Niven
		expr = xpath.compile("//book[contains(author,'Niven')]");
		result = expr.evaluate(doc, XPathConstants.NODESET);
		nodes = (NodeList) result;
		for (int i = 0; i < nodes.getLength(); i++) {
			System.out.println(nodes.item(i)
								.getChildNodes()
								.item(1)				//node <title> is on first index
								.getTextContent());
		}

		System.out.println("//11) Get book titles written by Neal Stephenson");

		// 11) Get book titles written by Neal Stephenson
		expr = xpath.compile("//book[author='Neal Stephenson']/title/text()");
		result = expr.evaluate(doc, XPathConstants.NODESET);
		nodes = (NodeList) result;
		for (int i = 0; i < nodes.getLength(); i++) {
			System.out.println(nodes.item(i).getNodeValue());
		}

		System.out.println("n//12) Get count of book titles written by Neal Stephenson");

		// 12) Get count of book titles written by Neal Stephenson
		expr = xpath.compile("count(//book[author='Neal Stephenson'])");
		result = expr.evaluate(doc, XPathConstants.NUMBER);
		count = (Double) result;
		System.out.println(count.intValue());

		System.out.println("n//13) Reading comment node ");

		// 13) Reading comment node
		expr = xpath.compile("//inventory/comment()");
		result = expr.evaluate(doc, XPathConstants.STRING);
		String comment = (String) result;
		System.out.println(comment);
	}
}

```

程序输出：

```java
//1) Get book titles written after 2001
Burning Tower

//2) Get book titles written before 2001
Snow Crash
Zodiac

//3) Get book titles cheaper than 8 dollars
Burning Tower
Zodiac

//4) Get book titles costlier than 8 dollars
Snow Crash

//5) Get book titles added in the first node
Snow Crash

//6) Get book title added in last node
Zodiac

//7) Get all writers
Neal Stephenson
Larry Niven
Jerry Pournelle
Neal Stephenson

//8) Count all books titles
3

//9) Get book titles with writer name start with Neal
Snow Crash
Zodiac

//10) Get book titles with writer name containing Niven
Burning Tower
//11) Get book titles written by Neal Stephenson
Snow Crash
Zodiac

//12) Get count of book titles written by Neal Stephenson
2

//13) Reading comment node
Test is test comment

```

希望本 xpath 教程对您有所帮助。 它将帮助您使用 Java 执行 **xpath**。 字符串的 **Java xpath 示例**也将在 **Java8** 中成功运行。

如果您有任何建议，请发表评论。

学习愉快！

推荐阅读：

[http://www.w3.org/TR/xpath-full-text-10-use-cases](https://www.w3.org/TR/xpath-full-text-10-use-cases/ "xpath use cases")

[http://en.wikipedia.org/wiki/XPath](https://en.wikipedia.org/wiki/XPath "wiki link")

[http://oreilly.com/catalog/xmlnut/chapter/ch09.html](https://oreilly.com/catalog/xmlnut/chapter/ch09.html)