3 定位路径与路径步
XPath的主要构件是表达式,其中,最重要的表达式是定位路径(Location Path)表达式,这也是它为什么命名为XPath的原因。定位路径表达式是这种类型的表达式:
/pub/book/author
粗看起来,定位路径表达式很像文件系统中的路径。定位路径的思想和文件系统中的路径却是很相似,当然,更复杂一些。
定位路径有两种,分别是相对的定位路径和绝对的定位路径。每种定位步都是由一个或多个定位步组成,每个定位步之间用正斜杠( / )分开。绝对路径以正斜杠( / )开始,而相对路径则没有。
XPath中用上下文节点集合上下文节点来描述定位路径的求值过程是如何进行的。上下文节点集定义为:表达式中给定点确定的当前节点集;而上下文则定义为:正在处理的当前节点。
定位步按照顺序从左到右一次一个地求值。每一个定位步都是相对于上下文节点集中的节点进行求值的,如果定位路径是绝对的,则初始的上下文节点集中包含根节点,否则,它包含当前上下文节点(即依赖于表达式使用的位置)。第一个定位步把上下文节点集中的每个节点当作上下文节点进行求值,然后结果节点集被合并为新的节点集,这个节点集成为下一步操作的上下文节点集,这样的处理在定位路径的每一个定位步中持续进行,最后的定位步产生的节点集就是这个表达式的结果。
一个定位路径由若干个定位步组成,而一个定位步有三个部分:
·????? 一个轴,它指定了定位步选择的节点与上下文节点之间树状关系,
·????? 一个节点测试,它指定定位步选择的节点的节点类型以及节点扩展名扩展名,和
·????? 零个或零个以上的判定词,它使用专有的表达式进一步细化定位步选择的节点集合。
定位步的句法是由两个冒号分开的轴名和节点测试组成,其后可跟随零个或零个以上在方括符内的表达式。例如,在child::para[position()=1] 中,child是轴名,para是节点测试而 [position()=1]则是判定词。
整个定位步选择的求值是先从轴和节点测试产生初始的节点集合,然后再由各个判定词的依次过滤,得到作为结果的节点集合。
初始的节点集合的节点组成、这些节点与上下文节点关系由轴指定的,其节点类型及扩展名由节点测试指定。例如,定位步descendant::para选择上下文节点的para元素后裔:descendant指定在初始的节点集合中的每一个节点都必须是该上下文的一个后裔节点;para 指定在初始的节点集合中的每一个节点都必须是para元素。
初始的节点集合由第一个判定词过滤后产生一个新的节点集合,新的节点集合再由第二个判定词进行过滤,如此一直下去。最后的节点集合是由定位步选择的节点集合。
以下是可用到的轴:
·???? child 轴包含上下文节点的孩子;
·???? descendant 轴包含上下文节点的后裔;后裔是一个孩子或一个孩子的一个孩子,等等,这样,后裔轴从来不包含属性或命名空间节点;
·???? parent 轴包含上下文节点的父节点,如果有的话;
·???? ancestor 轴包含上下文节点的祖先;上下文节点的祖先由上下文节点的父节点以及父节点的父节点等等组成;这样,祖先轴将总是包括根节点,除非上下文节点是根节点;
·???? following-sibling 轴包含上下文节点的所有在其后的兄弟,如果上下文节点是属性节点或命名空间节点,following-sibling 轴则为空;
·???? preceding-sibling 轴包含上下文节点的所有在其前的兄弟,如果上下文节点是属性节点或命名空间节点,preceding-sibling 轴则为空;
·???? following 轴包含在上下文节点所在的同一文档中,所有依照文档顺序在上下文节点后的节点,但排除所有的后裔,也排除属性节点以及命名空间节点;
·???? preceding 轴包含在上下文节点所在的同一文档中,所有依照文档顺序在上下文节点前的节点,但排除所有的后裔,也排除属性节点以及命名空间节点;
·???? attribute 轴包含上下文节点的属性,除非上下文节点是元素,该轴将为空;
·???? namespace 轴包含上下文节点的命名空间节点,除非上下文节点是元素,该轴将为空;
·???? self 轴只是包含上下文节点自己;
·???? descendant-or-self 轴包含上下文节点和上下文节点的后裔;
·???? ancestor-or-self 轴包含上下文节点和上下文节点的祖先;这样,该轴将总是包括根节点;
ancestor,descendant,following,preceding 以及 self 轴划分了一个文档(忽略属性和命名空间节点):他们相互不重叠,而且他们组在一起则包含了文档所有的节点。
每个轴有一个方向。轴的方向可以使向前(forward)的或者反向(reverse)的,向前的轴以文档顺序在节点间移动,而反向的轴则以反文档顺序在节点间移动。在这些轴里,child、descendant、descendant-or-self、following、following-sibling属于向前轴,而parent、ancestor、ancestor-or-self、preceding和preceding-sibling属于反向轴,其它的轴的方向不可用。
每个轴都有一个基本节点类型。如果轴能包含元素,基本的节点类型则是元素;否则,它的类型是轴能包含的节点的类型。因此,对于属性轴来说,基本的节点类型是属性。对于命名空间轴,基本的节点类型是命名空间。对于其它的轴,基本的节点类型是元素。
节点车使用与确定轴中的节点。如果给定节点的节点测试值为true,则它保留在节点集中,否则,它从节点集中移除。可以使用名称或类型进行节点测试。
在使用名称进行节点测试时,所有非指定轴基本类型的节点都被自动丢弃,然后剩下节点的名称与定位步中指定的限定名(Qname)进行比较,Qname被展开为一个命名空间名称(本地名称+命名空间URI),然后与问题中每个节点的命名空间名称进行比较。如果命名空间名称相配,则节点保留在节点集中,否则节点被丢弃。例如,child::para选择了上下文节点的para元素孩子,如果上下文节点没有para孩子,它将选择节点的一个空集合;attribute::href选择了上下文节点的href属性,如果上下文节点没有href属性,它将选择节点的一个空集合。
XPath处理程序为了将Qname扩展成命名空间名称,它需要访问命名空间绑定。因此,XPath处理程序需要提供创建命名空间绑定的机制,它将在对表达式求值时用到。在XSLT中,这可以通过标准的XML 1.0命名空间生命来完成,而在DOM中,则需要用其他的方法来完成。
对于基本节点类型的任何节点,节点测试 * 都为真。例如,child::* 将选择上下文的所有的元素子节点,而 attribute::* 将选择上下文节点的所有的属性。节点测试可以用格式 NCName:*。在这种情况下,前缀就以与QName一样的方法被扩展,即使用上下文命名空间声明。这个节点测试可以确定所有来自给定命名空间中的节点。
在问题中如果节点具有特定的类型则可以使用类型进行节点测试,XPath中定义了多个用于节点测试的节点类型标识符。对于任何正文节点,节点测试text()为真。例如,child::text() 将选择上下文节点的文本子节点。同样,对于注释节点,节点测试comment()为真;对于处理指令,节点测试processing-instruction()为真;processing-instruction()测试可以有一个Literal 的参数,在这种情况下,对于所有的处理指令,如指令名与参数相等,其值为真。对于任何节点,无论其类型,节点测试node()为真。
为此被置于定位步末端的方括号中。谓词筛选一个节点集以生成新的节点集。对于节点集中每一个被筛选的节点,谓词表达式将此节点作为上下文节点进行求值,结果强制转换为布尔值。如果结果为true,这节点保留在节点集中,否则,它将从节点集中被移除。
很多路径表达式可以使用简缩句法。实际上缩减句法比完整句法更常用,因为它们更简洁。最重要的简化是child::能从定位步中省略掉。实际效果上,child是缺省轴。例如,地址路径div/para是child::div/child::para的缩写。属性也有缩写形式:attribute:: 能被缩写成@。例如,定位路径para[@type="warning"]的完整形式为 child::para[attribute::type="warning"] 的。//是/descendant-or-self::node()/的缩写。例如,//para是/descendant-or-self::node()/child::para 的缩写,因此选择文档中所有的para元素(即使 para 元素是文档元素,也会被 //para 所选择,因为文档元素是根节点的孩子);div//para是div/descendant-or-self::node()/child::para的缩写,因此将选择div孩子的所有para后裔。要注意的是,地址路径//para[1]与地址路径 /descendant::para[1]的含意不一样,后者选择第一个后裔的para元素,前者选择这样的后裔para 元素:它们是其父节点的第一个para孩子。
此外,定位步 . 是 self::node() 的缩写。这与 // 一起使用特别有用。例如,定位路径 .//para 是 self::node()/descendant-or-self::node()/child::para 的缩写,因此将选择上下文节点的所有的para后裔元素。同样,地址路径 .. 是parent::node()的缩写。例如,../title是 parent::node()/child::title 的缩写,因此这将选择上下文节点的父节点的title孩子。
另外,谓词中的position()=数字也经常简写为数字,例如author[position=1]常简写为author[1],它用来选择上下文节点的第一个author子元素。
-----------------------------to be continued-----------------------------------