因为XML用于处理多种数据,所以必须有一种可以在XML代码中定位数据的方式。这个问题的答案就是XPath,它是专门用于定位匹配模式的一个或多个节点的小语言。尽管关于XPath深入的讨论已经超出本书的范围,不过还是要进行一些简单的介绍。
15.2.1 XPath简介
每个XPath表达式都有两部分:一个上下文节点和一个节点模式。上下文节点提供了节点模式起始的位置。节点模式是由一个或多个节点选择器组成的字符串。
例如,考虑以下XML文档:
同时考虑这个XPath表达式:
如果上下文节点是
<employees/>,则前面的XPath表达式就匹配了
<name>Nicholas C. Zakas</name>和
<name>Jim Smith</name>。在这个表达式中,
employee和
name都表示XML元素的标签名,按照它们在上下文节点中出现的顺序;斜杠表示从父节点到子节点的关系。这个XPath表达式表示:从
<employees/>起,匹配位于
<employee/>元素下的子节点
<name/>元素。
要选择地
<employee/>元素的第一个
<name/>元素,表达式要变成:
在XPath中,方框记号用于为某个节点提供更加确切的信息。这个例子使用了XPath的
position()函数,它用于返回元素在父节点下的位置。第一个子节点的位置为
1,所以将
position()和
1进行比较则只能匹配第一个
<employee/>元素。然后,斜杠和
name匹配在第一个
<employee/>元素下的
<name/>元素。
除位置和名称外,还可以使用不同的方法来匹配元素。假设要选择所有
title特性等于"
Salesperson"的
<employee/>元素,则XPath表达式变成:
在表达式中,
@是
attribute的缩写。
XPath是一种十分强大的表达式可以令在DOM文档中查找指定节点变得很容易。因此,IE和Mozilla都在DOM实现中引入了XPath支持。
如果想了解更多关于
XPath
的信息
,
请参阅
XPath 2.0: Programmer’s Reference(Wiley出版社,ISBN 0-7645-6910-4)。
15.2.2 IE中的XPath支持
看来,微软对直接在XML DOM对象中建立XPath支持感觉很惬意。每个节点都有两个可用于获取匹配XPath模式的节点的方法:
selectNodes(),用于返回匹配某个模式的节点的集合;
selectSingleNode()
,用于返回匹配给定模式的第一个节点。
使用前面一节中的数据,可用下面的代码选择所有
<employee/>元素下的
<name/>元素:
因为
selectNodes()是作为
oXmlDom.documentElement的方法调用的,所以文档元素被看作XPath表达式的上下文节点。方法返回包含所有匹配给定模式的节点的
NodeList,也就是说,可以这样迭代所有的元素:
如果没有匹配给定模式的节点,还是会返回
NodeList。如果为空,则它的
length
属性值等于0。
selectNodes()的返回结果是活的列表,所以如果用另外一个匹配XPath表达式的节点更新该文档,则这个元素会自动被添加到NodeList的合适位置。
如果只需要匹配模式的第一个元素,则可以使用
selectSingleNode():
如果发现了节点,则
selectSingleNode()方法返回一个
Element,否则它返回
null。
15.2.3 Mozilla中的XPath支持
Mozilla是根据DOM标准来实现对XPath的支持的。DOM Level 3附加标准DOM Level 3 XPath定义了用于在DOM中计算XPath表达式的接口。遗憾的是,这个标准要比微软直观的方式复杂得多。
虽然有好多与XPath相关的对象,最重要的两个是:
XPathEvaluator和
XPathResult。
XpathEvaluator
利用方法
evaluate()计算XPath表达式。
evaluate()方法有五个参数:XPath表达式、上下文节点、命名空间解释程序和返回的结果的类型,同时,在XPathResult中存放结果(通常为
null)。
命名空间解释程序,只有在XML代码用到了XML命名空间时才是必要的,所以通常留空,置为
null。返回结果的类型,可以是以下十个常量值之一:
q
XPathResult.ANY_TYPE——返回符合XPath表达式类型的数据;
q
XPathResult.ANY_UNORDERED_NODE_TYPE——返回匹配节点的节点集合,但顺序可能与文档中的节点的顺序不匹配;
q
XPathResult.BOOLEAN_TYPE——返回布尔值;
q
XPathResult.FIRST_ORDERED_NODE_TYPE——返回只包含一个节点的节点集合,且这个节点是在文档中第一个匹配的节点;
q
XPathResult.NUMBER_TYPE——返回数字值;
q
XPathResult.ORDERED_NODE_ITERATOR_TYPE——返回匹配节点的节点集合,顺序为节点在文档中出现的顺序。这是最常用到的结果类型;
q
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE——返回节点
集合快照,在文档外捕获节点,这样将来对文档的任何修改都不会影响这个节点列表。节点集合中的节点与它们出现在文档中的顺序一样;
q
XPathResult.STRING_TYPE——返回字符串值;
q
XPathResult.UNORDERED_NODE_ITERATOR_TYPE——返回匹配节点的节点集合,不过顺序可能不会按照节点在文档中出现的顺序排列;
q
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE——返回节点集合快照,在文档外捕获节点,这样将来对文档的任何修改都不会影响这个节点列表。节点集合中的节点和文档中原来的顺序不一定一样。
指定的结果类型决定了如何获取结果的值。下面是个典型的例子:
这个例子使用了
XPathResult. ORDERED_NODE_ITERATOR_TYPE结果,它是最常用到的结果类型。如果没有节点匹配XPath表达式,
evaluate()返回
null;否则,它返回一个
XPathResult对象。如果结果是个节点迭代子,不管它是有序的还是无序的,都可以不断用
iterateNext()方法获取在结果中的每一个匹配的结果。如果没有更多匹配的节点,
iterateNext()返回
null。可以用节点迭代子为Mozilla创建一个
selectNodes()方法:
给
Element类添加
selectNodes()方法以模仿IE中的行为。调用
evaluate()时,把用
this关键字作为上下文节点(这也是IE的工作方式)。然后,在结果数组(
aNodes)中放入匹配的节点。新方法用法如下:
如果指定了快照结果类型(不管是有序的还是无序的),都可使用
snapshotItem()以及
snapshotLength()方法,如下例所示:
在这个例子中,
snapshotLength返回节点的数量,
snapshotItem()返回快照中给定位置上的节点(类似于
NodeList的
length和
item()).
XPathResult.FIRST_ORDERED_NODE_TYPE结果返回第一个匹配的节点,可通过
singeNode Value特性来访问:
你肯定已猜到,这段代码可用来模仿IE的
selectSingleNode()方法:
这个方法的用法与在IE中的一样:
XpathResult
类型最后部分就是布尔类型、数字类型和字符串类型。其中,每个结果类型中都用相应的
booleanValue、
numberValue和
stringValue来返回单个值。对于布尔类型,一般返回
true,只需有一个节点匹配了XPath表达式,否则返回
false:
在此例中,如果任何节点匹配了"
employee/name",
booleanValue特性都等于
true。
对于数字类型,XPath表达式必须使用返回数字的XPath函数,诸如
count(),它可用于计算匹配给定模式的节点的个数:
这段代码输出了匹配模式"
employee/name"(2个)。如果不使用这种特殊的XPath函数便使用这个方法,
numberValue则等于
NaN。
对于字符串类型,
evaluate()方法查找第一个匹配XPath表达式的节点,然后返回第一个子节点的值(假设第一个子节点是文本节点)。否则,结果是空字符串。例:
前面的代码将输出"
Nicholas C. Zakas",因为这是在
<employee/>元素下
<name/>元素中的第一个文本节点。
如果觉得这比较危险,可以使用
XPathResult.ANY_TYPE。通过指定结果类型,使
evaluate()根据XPath表达式返回最合适的结果类型。一般来说,这个结果类型是个布尔值,字符串值或者是无序的节点迭代子。要判断返回的结果类型,可使用
resultType特性:
你已经看出来了,Mozilla中的XPath计算远比IE的复杂,当然也更加强大。通过使用自定义的
selectNodes()和
selectSingleNode()方法,可以在两种浏览器上使用同样的代码进行XPath计算。