XPath 简介
什么是XPath
XPath路径表达式
XPath使用路径表达式来选取XML文档中的节点或者节点集,这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
XPath节点
在XPath中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML文档是被作为节点树来对待的,树的根被称为文档节点或者根节点。
示例:
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>
上面的XML文档中的节点例子:
<bookstore> (文档节点)
<author>J K. Rowling</author> (元素节点)
lang="en" (属性节点)
节点关系
父(Parent)
每个元素以及属性都有一个父节点。
在上面的例子中,book 元素是 title、author、year 以及 price 元素的父节点。
子(Children)
元素节点可有零个、一个或多个子节点。
在上面的例子中,title、author、year 以及 price 元素都是 book 元素的子节点。
同胞(Sibling)
拥有相同的父的节点。
在上面的例子中,title、author、year 以及 price 元素都是同胞。
先辈(Ancestor)
某节点的父、父的父,等等。
在上面的例子中,title 元素的先辈有 book 元素和 bookstore 元素。
后代(Descendant)
某个节点的子,子的子,等等。
在上面的例子中,bookstore 的后代是 book、title、author、year 以及 price 元素。
XPath语法
选取节点
XPath使用路径表达式在XML文档中选取节点。节点是通过沿着路径或者step来选取的。
下面列出了最有用的路径表达式:
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:
路径表达式 | 结果 |
---|---|
bookstore | 选取 bookstore 元素的所有子节点。 |
/bookstore | 选取根元素 bookstore。 注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! |
bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素。 |
//book | 选取所有 book 子元素,而不管它们在文档中的位置。 |
bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 |
//@lang | 选取名为 lang 的所有属性。 |
谓语(Predicates)
谓语用来查找某个特定的节点或者包含某个指定的值的节点,谓语被嵌在方括号中。
在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:
路径表达式 | 结果 |
---|---|
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
/bookstore/book[position()<3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 |
//title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 |
//title[@lang=’eng’] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 |
/bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 |
/bookstore/book[price>35.00]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 |
选取未知节点
XPath通配符可用来选取未知的XML元素。
通配符 | 描述 |
---|---|
* | 匹配任何元素节点。 |
@* | 匹配任何属性节点。 |
node() | 匹配任何类型的节点。 |
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 | 结果 |
---|---|
/bookstore/* | 选取 bookstore 元素的所有子元素。 |
//* | 选取文档中的所有元素。 |
//title[@*] | 选取所有带有属性的 title 元素。 |
选取若干路径
通过在路径表达式中使用”|”运算符,可以选取若干个路径。
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 | 结果 |
---|---|
//book/title | //book/price | 选取 book 元素的所有 title 和 price 元素。 |
//title | //price | 选取文档中的所有 title 和 price 元素。 |
/bookstore/book/title | //price | 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。 |
XPath轴
轴可定义相对于当前节点的节点集。
轴名称 | 结果 |
---|---|
ancestor | 选取当前节点的所有先辈(父、祖父等)。 |
ancestor-or-self | 选取当前节点的所有先辈(父、祖父等)以及当前节点本身。 |
attribute | 选取当前节点的所有属性。 |
child | 选取当前节点的所有子元素。 |
descendant | 选取当前节点的所有后代元素(子、孙等)。 |
descendant-or-self | 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。 |
following | 选取文档中当前节点的结束标签之后的所有节点。 |
namespace | 选取当前节点的所有命名空间节点。 |
parent | 选取当前节点的父节点。 |
preceding | 选取文档中当前节点的开始标签之前的所有节点。 |
preceding-sibling | 选取当前节点之前的所有同级节点。 |
self | 选取当前节点。 |
XPath 运算符
下面列出了可用在 XPath 表达式中的运算符:
运算符 | 描述 | 实例 | 返回值 |
---|---|---|---|
| | 计算两个节点集 | //book | //cd | 返回所有拥有 book 和 cd 元素的节点集 |
+ | 加法 | 6 + 4 | 10 |
- | 减法 | 6 - 4 | 2 |
* | 乘法 | 6 * 4 | 24 |
div | 除法 | 8 div 4 | 2 |
= | 等于 | price=9.80 | 如果 price 是 9.80,则返回 true。如果 price 是 9.90,则返回 false。 |
!= | 不等于 | price!=9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。 |
< | 小于 | price<9.80 | 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。 |
<= | 小于或等于 | price<=9.80 | 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。 |
> | 大于 | price>9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。 |
>= | 大于或等于 | price>=9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.70,则返回 false。 |
or | 或 | price=9.80 or price=9.70 | 如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 false。 |
and | 与 | price>9.00 and price<9.90 | 如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 false。 |
mod | 计算除法的余数 | 5 mod 2 | 1 |
完整示例
使用dom4j解析以下xml文件:
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
Java代码:
import java.io.File;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Text;
import org.dom4j.io.SAXReader;
/**
* 使用XPath查找节点
*
* @author 小明
*
*/
public class XPathTest {
public static void search() throws Exception {
// 创建SAXReader对象
SAXReader reader = new SAXReader();
// 创建Document对象
Document document = reader.read(new File("books.xml"));
/* 查找所有的书籍的title */
List nodes = document.selectNodes("//book/title");
// 迭代
Iterator it = nodes.iterator();
System.out.println("所有的书籍的title:");
while (it.hasNext()) {
Element item = (Element) it.next();
System.out.println(item.getText());
}
/* 查找所有书籍的price的文本 */
nodes = document.selectNodes("//book/price/text()");
// 迭代
it = nodes.iterator();
System.out.println("所有书籍的price的文本:");
while (it.hasNext()) {
Text item = (Text) it.next();
System.out.println(item.getText());
}
/* 查找所有 category="WEB" 的 book 节点,输出它们的 title */
nodes = document.selectNodes("//book[@category='WEB']/title");
// 迭代
it = nodes.iterator();
System.out.println("查找所有 category='WEB' 的 book 节点,输出它们的 title:");
while (it.hasNext()) {
Element item = (Element) it.next();
System.out.println(item.getText());
}
/* 查找所有 2005 年出版的 book 的 title */
nodes = document.selectNodes("//book[year=2005]/title");
it = nodes.iterator();
System.out.println("查找所有 2005 年出版的 book 的 title:");
while (it.hasNext()) {
Element item = (Element) it.next();
System.out.println(item.getText());
}
/* 查找第一本 category='WEB' 的 book 的 title */
nodes = document.selectNodes("//book[@category='WEB'][1]/title");
it = nodes.iterator();
System.out.println("查找第一本 category='WEB' 的 book 的 title:");
while (it.hasNext()) {
Element item = (Element) it.next();
System.out.println(item.getText());
}
/* 查找price 大于 35.00 的book 的 title 和 category属性 */
nodes = document
.selectNodes("//book[price>35.00]/title|//book[price>35.00]/@category");
it = nodes.iterator();
System.out.println("查找price 大于 35.00 的book 的 title 和 category属性:");
while (it.hasNext()) {
Object obj = it.next();
if (obj instanceof Element) { // title为元素
Element item = (Element) obj;
System.out.println(item.getText());
} else if (obj instanceof Attribute) { // category为属性
Attribute attr = (Attribute) obj;
System.out.print(attr.getValue() + " : ");
}
}
}
public static void main(String[] args) throws Exception {
search();
}
}
运行结果:
所有的书籍的title: Everyday Italian Harry Potter XQuery Kick Start Learning XML 所有书籍的price的文本: 30.00 29.99 49.99 39.95 查找所有 category='WEB' 的 book 节点,输出它们的 title: XQuery Kick Start Learning XML 查找所有 2005 年出版的 book 的 title: Everyday Italian Harry Potter 查找第一本 category='WEB' 的 book 的 title: XQuery Kick Start 查找price 大于 35.00 的book 的 title 和 category属性: WEB : XQuery Kick Start WEB : Learning XML