9.5 XPath 2.0新增的表达式

XPath 2.0新增了一些功能更强大的表达式,这些表达式主要有如下3种类型:

alt for表达式:用于循环访问序列中的每个项(Item)。

alt 条件表达式:用于处理分支情况。

alt 限定性表达式:用于判断序列中每项或任一项是否满足某个条件。

9.5.1 for表达式

for表达式主要用于循环访问序列中的每个项(Item),并对每项进行一次计算,最后将每项计算得到的结果组合成序列后返回。也就是说,for表达式循环访问的序列里包含多少项,计算得到的结果序列里就包含多少项。

for表达式的语法格式如下:

alt

从上面的语法格式可以看出,for表达式的实质应该是foreach表达式(或者也可称为迭代器),其中的$var将依次等于sequence里的每项,而rtExpression将是一个根据$var计算出来的结果值,因此sequence里的每项都将计算得到一个结果值,最后将多个结果值组合成序列后返回。

假设有如下XML文档,这份文档记录了一份考试成绩表:

程序清单:codes\09\9.5\scorelist.xml

alt

假设在进行XSLT转换时不仅需要访问每个科目的名称和成绩,还希望通过统计的方法计算该成绩单的平均成绩——计算平均成绩时,需要将<score…/>元素的值乘以<weight…/>(权重)的值,然后再计算平均值。这样的需求也就是要根据<subject…/>序列生成一个新的序列,使用for表达式就可以解决该问题,如下面的XSLT样式单文件所示:

程序清单:codes\09\9.5\score.xslt

alt

上面的粗体字代码使用for表达式处理subject序列,该序列一共包含3个<subject…/>元素。处理流程如下:

(1)对于第1个<subject…/>元素,计算$subject/score*$subject/weight)的值,将返回83.0。

(2)对于第2个<subject…/>元素,计算$subject/score*$subject/weight)的值,将返回55.2。

(3)对于第3个<subject…/>元素,计算$subject/score*$subject/weight)的值,将返回35.1。

(4)将前面3步计算得到的3个值重新组合成一个新的序列:(83.0,55.2,35.1)。

上面的样式单中最后使用avg()函数来求取新序列(83.0,55.2,35.1)的平均值,最终将返回该成绩单的平均成绩。在XMLSpy中使用上面的XSLT样式单来转换前面的XML文档可看到图9.1所示结果:

alt

图9.1 使用for表达式进行转换

实际上,for表达式不仅可以对一个序列进行循环,还可以对多个序列进行嵌套循环。例如使用for表达式遍历处理2个序列,其语法格式如下:

alt

上面的for表达式可以转换为如下伪码:

alt

对于遍历2个序列的for表达式,最终得到的序列包含n*m个项(假设第一个序列包含n个项,第二个序列包含m个项)。

如果遍历3个序列,其语法格式如下:

alt

与遍历2个序列类似,对于遍历3个序列的for表达式,最终得到的序列包含nml个项(假设第一个序列包含n个项,第二个序列包含m个项,第三个序列包含l个项)。


alt注意

在XPath序列里没有所谓的多维序列,序列里的所有项都是单个节点或基本值,而不可能还是序列。


假设有如下XML文档:

程序清单:codes\09\9.5\list.xml

alt

对于上面的XML文档,下面的XSLT示范了如何使用for表达式同时遍历两个序列:

程序清单:codes\09\9.5\list.xslt

alt

上面的粗体字代码分别遍历了前面XML文档里所有的<item…/>和<game…/>元素,因为item序列里包含3个元素,game序列里包含2个元素,因此使用for表达式计算得到的序列将包含6个元素,使用上面的XSLT转换前面的XML文档,将得到图9.2所示结果(一共有6个项)。

alt

图9.2 使用for表达式遍历2个序列

9.5.2 if表达式

XPath提供了if表达式来根据不同条件得到不同的返回值,if表达式的语法格式如下:

alt

在if表达式中,可以包含0个或多个else if(condition) then rtVal部分。

假设现在需要对前面的成绩单进行判断,如果考分高于90即认为该科目成绩为优秀,如果考分高于75即认为该科目成绩为良好,如果考分高于60即认为该科目成绩为及格,否则该科目成绩为不及格。可以在XSLT样式单中使用if表达式进行处理,如下所示:

程序清单:codes\09\9.5\if.xslt

alt

使用上面的XSLT样式单转换前面的XML文档,可看到图9.3所示结果。

alt

图9.3 使用if表达式进行转换

9.5.3 some/every判断表达式

XPath 2.0还提供了一种判断表达式,这种表达式的运行机制和for表达式差不多——它们本质上都是Ruby中的迭代器,因此判断表达式同样用于循环处理序列中的每项,与for表达式不同的是,判断表达式用于判断序列中的全部项或任一项是否满足某个条件。

判断表达式有两种形式:

alt some $var in sequence satisfies condition:只要sequence中任一项满足condition条件,该判断表达式将返回true。

alt every $var in sequence satisfies condition:如果sequence中全部项都满足condition条件,该判断表达式将返回true。

上面两个判断表达式的语法格式和处理流程基本上完全相同,不同的是some表达式只要求序列中任一项满足condition条件即可返回true;而every表达式则要求序列中所有项都满足condition条件才会返回true。

类似于for表达式可以对多个序列进行遍历,some/every判断表达式也支持对多个序列进行遍历,语法格式如下:

alt

上面的语法格式同样可转换为如下伪码:

alt

对于some表达式而言,只要上面的循环中任意一次循环返回true,则整个判断表达式都将返回true;而对于every表达式而言,只要上面的循环中任意一次循环返回false,则整个判断表达式都将返回false。

假设依然需要对前面的保存考试成绩单的XML文档进行转换,如果所有科目的考试成绩都大于90,则输出“all subject > 90”,否则输出“some subject <=90”。可以使用every判断表达式,如以下XSLT样式单代码所示:

程序清单:codes\09\9.5\every.xslt

alt

如果不需要所有科目的考试成绩都大于90,只要任意一科的考试成绩大于90,则可使用some判断表达式,如以下样式单代码所示:

程序清单:codes\09\9.5\some.xslt

alt

some/every判断表达式总是返回一个boolean值,因此在上面两份XSLT样式单中,some/every判断表达式的值被作为if表达式的判断条件来使用。