xpath1.0和2.0
XPath 2.0和XSLT 2.0中的主要新概念之一是,一切都是序列。 在XPath 1.0和XSLT 1.0中,通常使用节点树。 解析的XML文档是一棵包含文档节点及其后代的树。 使用该节点树,您可以找到根元素的节点,以及所有根元素的后代,属性和同级。 (XML文件的根元素之外的任何注释或处理指令都被视为根元素的同级。)
在XPath 2.0和XSLT 2.0中使用XML文档时,使用序列的方式与XPath 1.0和XSLT 1.0中的树结构相同。 该序列包含一个项目(文档节点),并且您将始终以相同的方式使用它。 但是,您可以创建原子值序列。 清单1摘自即将到来的示例应用程序,您将在其中管理16队单淘汰赛的数据,该示例显示了一系列原子值的示例。
清单1.原子值序列
<xsl:variable name="seeds" as="xs:integer*">
<xsl:sequence
select="(1, 16, 8, 9, 5, 12, 4, 13, 6, 11, 3, 14, 7, 10, 2, 15)"/>
</xsl:variable>
这段代码定义了变量$seeds
。 如您所料,新的<xsl:sequence>
元素定义了一系列项目。 在这种情况下,这些项目是XML Schema xs:integer
。 新的as
属性定义变量的数据类型,星号( xs:integer*
)表示该序列包含零个或多个整数。 在XPath 1.0和XSLT 1.0中,您将创建16个不同的文本节点,然后将这些节点分组为一个变量。 在XPath 2.0和XSLT 2.0中,该序列充当一维数字数组,这正是示例应用程序所需要的。
序列遵循两个规则。 首先,它们不能包含其他序列。 如果从三个项目的序列中创建一个新序列,然后再从三个项目的序列中创建一个新序列,则结果将是六个项目的新序列。 其次,序列允许您混合节点和项目。 您可以创建一个包含清单1中所示的原子值以及所有<contestant>
元素的序列,如清单2中所示 。
清单2.一系列节点和原子值
<xsl:variable name="seeds" as="item()*">
<xsl:for-each select="/bracket/contestants/contestant">
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:sequence
select="(1, 16, 8, 9, 5, 12, 4, 13, 6, 11, 3, 14, 7, 10, 2, 15)"/>
</xsl:variable>
变量$seeds
包含所有竞争者节点和您先前使用的16个原子值。 请注意,变量的数据类型为item()*
。 项目是节点或原子值,因此此变量可以包含任何内容。
现在您已经了解了序列和项目的基础,让我们看一下to
运算符,它是XPath 2.0和XSLT 2.0中的新增功能。 它允许您选择整数范围。 例如,您可以创建一个清单3所示的序列。
清单3.使用to
运算符创建的整数序列
<xsl:variable name="range" as="item()*">
<xsl:sequence select="1 to 16"/>
</xsl:variable>
这段代码创建了一个名为$range
的变量,该变量包含从1到16的整数。在示例应用程序中,可以将to
运算符用作循环机制,如清单4所示。
清单4.使用to
运算符进行循环
<xsl:for-each select="1 to 32">
<!-- Do something useful here -->
</xsl:for-each>
在构建样式表之前,请更详细地查看示例应用程序。
了解示例应用程序
在本文的示例应用程序中,您将管理16队单淘汰赛的数据。 如您所料,锦标赛数据以XML表示。 您将创建一个XSLT 2.0样式表,该样式表将XML数据转换为演示锦标赛结果HTML表。 清单5显示了XML文档格式。
清单5.带有锦标赛数据的XML文档
<?xml version="1.0" encoding="UTF-8"?>
<!-- tourney.xml -->
<bracket>
<title>RSDC Smackdown</title>
<contestants>
<contestant seed="1" image="images/homerSimpson.png">Donuts</contestant>
<contestant seed="2" image="images/caffeine.png">Caffeine</contestant>
<contestant seed="3" image="images/fearlessFreep.png">Fearless Freep</contestant>
<contestant seed="4" image="images/wmd.jpg">Weapons of Mass Destruction</contestant>
<contestant seed="5" image="images/haroldPie.jpg">Pie</contestant>
<contestant seed="6" image="images/adamAnt.png">Adam Ant</contestant>
<contestant seed="7" image="images/georgeWBush.jpg">Misunderestimated</contestant>
<contestant seed="8" image="images/sillyPutty.jpg">Silly Putty</contestant>
<contestant seed="9" image="images/krazyGlue.jpg">Krazy Glue</contestant>
<contestant seed="10" image="images/snoopDogg.png">Biz-Implification</contestant>
<contestant seed="11" image="images/atomAnt.png">Atom Ant</contestant>
<contestant seed="12" image="images/ajaxcan.png">AJAX</contestant>
<contestant seed="13" image="images/darthVader.jpg">Darth Vader</contestant>
<contestant seed="14" image="images/nastyCanasta.png">Nasty Canasta</contestant>
<contestant seed="15" image="images/jcp.png">Java Community Process</contestant>
<contestant seed="16" image="images/andre.png">Andre the Giant</contestant>
</contestants>
<results>
<result round="1" firstSeed="1" secondSeed="16" winnerSeed="1"/>
<result round="1" firstSeed="8" secondSeed="9" winnerSeed="9"/>
<result round="1" firstSeed="5" secondSeed="12" winnerSeed="5"/>
<result round="1" firstSeed="4" secondSeed="13" winnerSeed="4"/>
<result round="1" firstSeed="6" secondSeed="11" winnerSeed="11"/>
<result round="1" firstSeed="3" secondSeed="14" winnerSeed="3"/>
<result round="1" firstSeed="7" secondSeed="10" winnerSeed="10"/>
<result round="1" firstSeed="2" secondSeed="15" winnerSeed="2"/>
<result round="2" firstSeed="1" secondSeed="9" winnerSeed="1"/>
<result round="2" firstSeed="5" secondSeed="4" winnerSeed="5"/>
<result round="2" firstSeed="11" secondSeed="3" winnerSeed="3"/>
<result round="2" firstSeed="10" secondSeed="2" winnerSeed="2"/>
<result round="3" firstSeed="1" secondSeed="5" winnerSeed="1"/>
<result round="3" firstSeed="3" secondSeed="2" winnerSeed="2"/>
<result round="4" firstSeed="1" secondSeed="2" winnerSeed="2"/>
</results>
</bracket>
在<title>
元素中,您可以看到锦标赛的名称RSDC Smackdown。 本文档代表了今年IBM Rational Software Developer Conference上的一次会议的实际结果。
方括号包含16个<contestant>
元素,每个元素都有一个名称(其文本),一个种子和一个图像。 一个由16支球队组成的锦标赛包括15场比赛。 每个比赛由一个<result>
元素表示。 每个比赛都有四段数据:比赛发生的那round
比赛的round
( round
属性),两个参赛者的种子(存储在firstSeed
和secondSeed
属性中)以及获胜者的种子(the winnerSeed
属性)。 您的任务是获取这个XML文档,并将其转换为显示结果HTML表, 如图1所示。
图1.比赛结果显示在HTML表中
该表有32行和5列。 使用XSLT 1.0方法,您可以一次构建一行HTML表,如清单6所示。
清单6.一次一行地构建HTML表
<!-- Row 1 -->
<tr>
<td style="border: none; border-top: solid; border-right: solid;">
<xsl:text>[1] </xsl:text>
<xsl:value-of select="$contestants[@seed='1']/>
</td>
<td style="border: none;>
</td>
<td style="border: none;>
</td>
<td style="border: none;>
</td>
<td style="border: none;>
</td>
</tr>
<!-- Row 2 -->
. . .
那会起作用,但是样式表将很难维护。 在整个样式表中,重复每一行和每一列的代码。 这里的主要问题是输出表中需要32行。 32行中的每一行都包含来自XML文档中的元素( <contestant>
或<result>
)的数据。 不幸的是,您没有可以迭代的32个元素。 您可以使用<xsl:for-each select="contestants/contestant|results/result">
,但是这些元素不会按照您在表中需要的顺序显示。 XSLT 1.0没有很多工具可以帮助您。
您可以重构代码以使样式表更加简单。 单元格的样式和内容中包含一些模式,您可以使用XPath 2.0和XSLT 2.0的新功能来遍历这些模式。 最终的样式表比(公认的笨拙的)原始版本小大约70%。 在构建样式表之前,让我们看一下如何重构代码。
重构代码-计算表格单元格样式
要重构代码,请开始将样式信息移至CSS样式表。 如图2所示 ,HTML括号表具有五种不同的单元格样式: None
, MatchupStart
, MatchupMiddle
, MatchupEnd
和Solid
。
图2. HTML表中的单元格边框样式
清单7显示了CSS代码的样子。
清单7. CSS样式表
.None { width: 20%; }
.MatchupStart { width: 20%;
border: none; border-top: solid;
border-right: solid; }
.MatchupMiddle { width: 20%;
border: none; border-right: solid; }
.MatchupEnd { width: 20%;
border: none; border-bottom: solid;
border-right: solid; }
.Solid { width: 20%;
border: solid; }
边框样式易于查看比赛的结果。 连接两个单元的水平线表示这两个竞争者彼此面对。 在下栏中带有实线边框的单元格表示比赛的获胜者。 查看边框样式,您可以看到一个确定的模式:对于每对竞争对手,第一个竞争对手之前的单元格都没有边界(样式None
),两个竞争对手的单元格都有实线边界( Solid
样式),这些单元格两个竞争对手之间的边框在右边有一个边框(样式MatchupMiddle
),最后一个竞争对手之后的单元格没有边框(样式None
)。 这与第一列略有不同。 因为第一列中的一对竞争者是如此接近,所以第一个竞争对手的单元格在顶部和右侧都有边框(样式MatchupStart
),而第二个竞争对手的单元格在底部和右侧则带有边框(样式) MatchupEnd
)。
请注意,成对的竞争者在每一列中相距较远。 第1列的竞争者之间有一个单元格的间隙,第2列的竞争者之间有一个三单元格的间隙,第3列的竞争者之间有一个7单元格的间隙,第4列中的每个单元格的大小小于1。 2的幂,所以这里有一个确定的模式。
表格中的通用模式是每列包含重复的单元格组。 五个重复列的重复组的大小(为更好的术语,我将其称为列的周期 )分别为4、8、16、32和64。 每个重复组包含两个竞争者,两个竞争者之间的单元以及两个竞争者之前和之后的单元。
在每一列中,在计算中使用两个值: $period
,它代表重复组的大小; $oneQuarter
,它是周期大小的四分之一。 (将$period div 4
存储在变量中可使代码更简洁。) 表1显示了单元格边框样式的通用规则。
表1. HTML表中的单元格边框样式
式 | 第1栏(期间4) | 第2栏(期间8) | 第3栏(期间16) | 第4栏(期间32) | 第5栏(期间64) |
---|---|---|---|---|---|
$row < $oneQuarter or $row > $period - $oneQuarter | 不适用 | 第1行 ,样式None | 第1-3行 ,样式None | 第1至7行 ,样式None | 第1-15行 ,样式为None |
$row = $oneQuarter | 第1行 ,样式为MatchupStart | 第2行 , Solid 样式 | 第4行 , Solid 样式 | 第8行 , Solid 样式 | 第16行 , Solid 样式 |
$row > $oneQuarter and $row < $period - $oneQuarter | 第2行 ,样式MatchupMiddle | 第3-5行 ,样式为MatchupMiddle | 第5-11行 ,样式为MatchupMiddle | 第9-23行 ,样式为MatchupMiddle | 第17-32行 ,样式: None |
$row = $period - $oneQuarter | 第3行 ,样式MatchupEnd | 第6行 , Solid 样式 | 第12行 , Solid 样式 | 第24行 , Solid 样式 | 不适用 |
$row < $oneQuarter or $row > $period - $oneQuarter | 第4行 ,样式None | 第7-8行 ,样式为None | 第13-16行 ,样式为None | 第25-32行 ,样式为None | 不适用 |
现在,您已经创建了一种优雅的方法来找出表中任何给定单元格的样式。 给定列号和行号,该公式的作用就像一个超级按钮。
重构代码-计算表格单元格的内容
当然,在表中创建单元格时,还需要在该单元格中放入适当的内容。 那也有一个不错的模式。 样式为None
或MatchupMiddle
任何内容都根本没有任何内容。 这意味着您只需要担心使用其他三种样式为单元格找到正确的内容。
在表1中 ,您可以看到具有内容的单元格具有属性$row = $oneQuarter or $row = $period - $oneQuarter
。 对于第一列,您只需编写种子和相应参赛者的姓名。 配对基于种子,并具有表2中所示的配对。
表2.基于种子的匹配
根据种子配对 |
---|
[1]与[16] |
[8]与[9] |
[5]与[12] |
[4]与[13] |
[6]与[11] |
[3]与[14] |
[7]与[10] |
[2]与[15] |
安排比赛,以便如果总是获得较高的种子,则种子排名最高的球队将始终扮演剩下的种子人数最少的球队,种子排名第二的球队将始终扮演剩下的种子人数第二低的球队,依此类推。 以这种顺序查看种子(1、16、8、9、5、12、4、13、6、11、3、14、7、10、2、15),可以看到它们与序列$seeds
。 竞争对手按此顺序显示在第1列中。
竞争对手每隔一行出现一次,这意味着第1行具有序列中的第一个种子,第3行具有序列中的第二个种子,第5行具有序列中的第三个种子,依此类推。 这里的模式是$row + 1 div 2 = $index
。 换句话说,取行号,加1,然后除以2,得到$seeds
序列中种子的索引。 表格单元格的内容是种子编号,后跟带有该种子的参赛者的姓名。
这样就可以处理第1列的内容了。如您所料,第2列到第5列更加复杂。 您无需查看<contestant>
元素,而需要查看显示在15个<result>
元素中的<result>
。
第2列包含第一轮的获胜者。这意味着您需要查看具有属性round="1"
的<result>
元素。 为了简化起见,第一个比赛的获胜者(1到16种子)存储在第一个<result>
元素中,第二个比赛的获胜者(8到9种子)存储在第二个<result>
元素中, 等等。 第2列中的获胜者显示在第2、6、10、14、18、22、26和30行中。为寻找模式,第2行使用第一个<result>
元素,第6行使用第二个,而第10行使用第三个。 对于此列,如果采用行号,将其加2,然后除以4,就可以得到适当的<result round="1">
元素的位置。
第3列和第4列的处理方式类似。 第3列的获胜者显示在第4、12、20和28行中(此处的模式是将4加除以8)。 第4列的获胜者显示在第8行和第24行(加8并除以16)。 第5栏仅显示一名参赛者,即整个比赛的获胜者。 如果您位于第16行,则显示单个<result round="4">
元素中的获胜者。
查找所寻找值位置的重构方法是($row + $oneQuarter) div ($oneQuarter * 2)
。 使用不断增加的重复模式大小可以简化代码。 对于第1列,计算出的位置是种子序列的索引; 对于其他列,计算出的位置是适当的<result>
元素的位置。
现在,您可以用一种优雅的方式来确定表中每个单元格的内容和样式。 给定行号和列号,您可以找出所有需要了解的内容。
利用XPath 2.0和XSLT 2.0的强大功能
现在,您可以使用XPath 2.0和XSLT 2.0中可用的新技术来简化样式表。 首先,使用to
运算符。 如果使用过程编程语言构建表,则可能会执行类似于清单8的操作 。
清单8.样式表的过程方法
for (int row=1; row<=32; row++)
for (int column=1; column<=5; column++)
// Build each cell in the table here
在XPath 2.0和XSLT 2.0中,您可以使用<xsl:for-each>
替换您在过程语言中使用的for
循环。 清单9显示了如何。
清单9.使用to
运算符for
循环
<xsl:for-each select="1 to 32">
<xsl:variable name="outerIndex" select="."/>
<tr>
<xsl:for-each select="1 to 5">
您需要在此处解决一些复杂问题。 首先,在XPath 1.0和XSLT 1.0中,使用<xsl:for-each>
更改每次迭代的上下文。 例如,如果您使用<xsl:for-each select="contestants/contestant>
,则上下文节点是每次迭代中最新的<contestant>
。当您使用to
运算符迭代各个整数时,上下文项(即。在2.0上下文项 ) 未定义正如你可以看到清单9 ,需要保存外的当前值<xsl:for-each>
,因为它不是提供给在所述内<xsl:for-each>
。
但情况变得更糟。 如果上下文项未定义, 则无法从文档中选择节点 。 如果您知道自己位于第1行第1列,则可以从$seeds
序列中获取第一项,因为$seeds
是全局变量。 这表明您需要找到<contestant seed="1">
元素。 不幸的是,您无法找到文档中的任何内容。 即使使用绝对的XPath表达式,例如/bracket/contestants/contestant[@seed='1']
也不起作用。 因此,您需要将您关心的节点存储为全局变量。 清单10显示了如何随时随地访问全局变量。
清单10.存储所需节点的全局变量
<xsl:variable name="results" select="/bracket/results"/>
<xsl:variable name="contestants" select="/bracket/contestants"/>
如果需要获取第16个种子竞赛者的名称,则XPath表达式为$contestants/contestant[@seed="16"]
。 您可以类似地访问<result>
元素。 如果您需要在第2轮中获得第二场比赛的获胜者,则表达式为$results/result[@round="2"][2]/@winnerSeed
。 清单11显示了两个可用于生成HTML表的全局变量。
清单11.其他有用的全局变量
<xsl:variable name="periods" as="xs:integer*">
<xsl:sequence select="(4, 8, 16, 32, 64)"/>
</xsl:variable>
<xsl:variable name="backgroundColors" as="xs:string*">
<xsl:sequence
select="('background: #CCCCCC;', 'background: #9999CC;',
'background: #99CCCC;', 'background: #CC99CC;',
'background: #CCCC99;')"/>
</xsl:variable>
这些变量存储每列的期间值和每列的背景色。 要使用每个变量,只需将列号用作所需的单个值的索引。
XPath 2.0和XSLT 2.0中的另一个不错的增强是<xsl:function>
元素。 让我们在样式表中创建两个函数: cellStyle
和getResults
。 第一个函数返回每个单元格的边框样式,第二个函数返回给定匹配的结果(如果有)。 这两个函数的参数都是单元格的行号和列号。 清单12演示了cellStyle
函数的代码。
清单12. cellStyle
函数
<xsl:function name="bracket:cellStyle" as="xs:string">
<xsl:param name="row" as="xs:integer"/>
<xsl:param name="column" as="xs:integer"/>
<xsl:variable name="period" as="xs:integer"
select="subsequence($periods, $column, 1)"/>
<xsl:variable name="oneQuarter" as="xs:integer"
select="$period div 4"/>
<xsl:variable name="lastColumn" as="xs:boolean"
select="$oneQuarter = count($contestants/contestant)"/>
<xsl:variable name="position" select="$row mod $period"/>
<xsl:value-of
select="if ($position = $oneQuarter) then
(if ($column = 1) then 'MatchupStart'
else 'Solid')
else if ($position = $period - $oneQuarter) then
(if ($column = 1) then 'MatchupEnd'
else 'Solid')
else if ($lastColumn) then 'None'
else if ($position < $oneQuarter or
$position > $period - $oneQuarter) then 'None'
else 'MatchupMiddle'"/>
</xsl:function>
在确定单元格样式之前,请使用四个变量。 您从全局变量$periods
检索$period
的值。 如前所述, $oneQuarter
只是$period div 4
。 布尔值$lastColumn
只是将$oneQuarter
与锦标赛中参赛者的数量进行比较。 如果这些值相等,则您正在处理最后一列。 最后,变量$position
表示模式中当前行的位置。 换句话说,表中的第9行是第1列和第2列的重复组的第一行。使用该行在模式中的位置以找出单元格样式。
XPath 2.0和XSLT 2.0具有if
运算符,该运算符具有一个表达式(用括号括起来),后跟一个then
和一个else
。 所有这些都进入<xsl:value-of>
的select
属性。 在此示例中,用单个表达式替换了更为冗长的<xsl:choose>
元素。 考虑到表公式的讨论,此处的代码非常简单。 如果逻辑更复杂,则使用<xsl:choose>
可能更易于维护,即使它需要更多类型输入。
<xsl:function>
一些语法说明:首先,您需要为函数声明一个新的名称空间。 如果在XPath表达式中调用方bracket:cellStyle()
,则名称空间将告诉XSLT 2.0处理器如何查找该函数。 其次,请注意,您使用了as="xs:string"
属性来指示此函数返回一个字符串。 <xsl:value-of>
元素返回五个样式名称之一; 那是函数的输出。
如清单13所示 , getResults
函数稍微复杂一点,但并不复杂。
清单13. getResults
函数
<xsl:function name="bracket:getResults" as="xs:string">
<xsl:param name="row" as="xs:integer"/>
<xsl:param name="column" as="xs:integer"/>
<xsl:variable name="period" as="xs:integer"
select="subsequence($periods, $column, 1)"/>
<xsl:variable name="oneQuarter" as="xs:integer"
select="$period div 4"/>
<xsl:variable name="position" select="$row mod $period"/>
<xsl:choose>
<xsl:when test="$position = $oneQuarter
or
$position = $period - $oneQuarter">
<xsl:variable name="round" select="$column - 1"/>
<xsl:variable name="index"
select="($row + $oneQuarter) div ($oneQuarter * 2)"/>
<xsl:variable name="currentSeed"
select="if ($column = 1) then
subsequence($seeds, $index, 1)
else
$results/result[@round=$round][$index]/@winnerSeed"/>
<xsl:choose>
<xsl:when test="string-length(string($currentSeed))">
<xsl:value-of
select="concat('[', $currentSeed, '] ',
$contestants/contestant[@seed=$currentSeed])"/>
</xsl:when>
<xsl:otherwise>
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
您具有与以前相同的参数。 您必须在此处对第1列进行一些特殊处理,该列包含<contestant>
元素的数据,而其他列则包含<result>
元素的数据。 与cellStyle
函数一样,您可以计算$period
和$position
,并使用$oneQuarter
变量简化公式。
如果单元格中包含竞赛者,则需要再计算三个变量。 $round
变量比列小1(第2列包含来自第1轮的结果),然后根据前面讨论的公式计算$index
变量。
您需要遵循两个步骤来找到适当的数据。 首先,设置$currentSeed
变量的值。 如果是第1轮,则可以使用新的子subsequence
函数从$seeds
变量中选择一个值。 在其他回合中,您将从相应的<result>
元素中获得winnerSeed
属性。
其次,除了以不同方式处理第1列之外,您还需要考虑<result>
元素没有获胜者的可能性( winnerSeed=""
)。 如果发生这种情况,请返回一个不间断的空格(  
)。 由于XSLT 2.0处理数据类型的方式(这是以后的文章的主题),因此需要将$currentSeed
转换$currentSeed
字符串,然后测试字符串的长度。 如果字符串的长度为零,则返回一个不间断的空格;否则,返回0。 否则,返回种子和参赛者的姓名。
最后,请注意您在此处使用<xsl:choose>
。 尽管您可以使用单个XPath if
语句完成所有操作,但是代码将很笨拙。 即使<xsl:choose>
更加冗长,代码也更加简洁易懂。
现在,您已经设置了所需的全局变量和函数,样式表的核心变得简单而优雅,如清单14所示 。
清单14.样式表的核心
<xsl:for-each select="1 to 32">
<xsl:variable name="outerIndex" select="."/>
<tr>
<xsl:for-each select="1 to 5">
<td style="{subsequence($backgroundColors, ., 1)}"
class="{bracket:cellStyle($outerIndex, .)}">
<xsl:value-of
select="bracket:getResults($outerIndex, .)"/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
您有两个循环为表的32行创建五列。 对于每个单元格,背景色基于当前列号。 cellStyle
函数确定边框样式( class="x"
),而getResults
函数确定单元格的值。
使用样式表
如您所料,需要使用XSLT 2.0处理器才能使用此样式表。 我不会在此处重新打印整个样式表,但是您必须使用<xsl:stylesheet version="2.0">
这样处理器才能在XSLT 2.0模式下运行。 我强烈推荐Michael Kay的Saxon处理器( 有关更多详细信息,请参阅参考资料 )。 Kay博士是XSLT 2.0规范的编辑,而Saxon在某种程度上是开发规范时的测试用例。 如果您使用Saxon,请使用以下命令使用样式表results-html.xsl
转换XML文件tourney.xml
并将输出写入results.html
文件:
java net.sf.saxon.Transform -o results.html tourney.xml results-html.xsl
为了说明样式表的灵活性,请取出XML文件中的一些结果并运行转换。 清单15显示了<result>
元素的外观。
清单15.具有不完整锦标赛数据的XML文档
<?xml version="1.0" encoding="UTF-8"?>
<!-- incomplete-tourney.xml -->
<bracket>
. . .
<results>
<result round="1" firstSeed="1" secondSeed="16" winnerSeed="1"/>
<result round="1" firstSeed="8" secondSeed="9" winnerSeed="9"/>
<result round="1" firstSeed="5" secondSeed="12" winnerSeed="5"/>
<result round="1" firstSeed="4" secondSeed="13" winnerSeed="4"/>
<result round="1" firstSeed="6" secondSeed="11" winnerSeed="11"/>
<result round="1" firstSeed="3" secondSeed="14" winnerSeed="3"/>
<result round="1" firstSeed="7" secondSeed="10" winnerSeed="10"/>
<result round="1" firstSeed="2" secondSeed="15" winnerSeed="2"/>
<result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="2" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="3" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="3" firstSeed="" secondSeed="" winnerSeed=""/>
<result round="4" firstSeed="" secondSeed="" winnerSeed=""/>
</results>
</bracket>
清单15表示第一轮比赛后的比赛状态。 第2、3和4回合的所有<result>
元素都有一个空的winnerSeed
属性。 尽管如此,样式表仍会生成正确的括号, 如图3所示 。
图3.不完整锦标赛的括号
摘要
本文演示了XPath 2.0和XSLT 2.0的许多新功能。 在示例应用程序中,您获取了一个笨拙的样式表,并将其重构为更小,更易维护的代码段。 您的一部分工作是分析代码以查找模式,但是如果没有to
运算符<xsl:function>
和项目序列,那么就很难像您那样精简样式表。
最重要的是,您创建了可以处理任意数量参与者的通用函数。 例如,要更改样式表以处理32队比赛,您需要更改序列$seeds
和$periods
。 您还需要将select="1 to 5"
替换为select="1 to $rounds"
,其中$rounds
代表锦标赛中的回合数。 当然,最优雅的解决方案是创建XSLT 2.0函数,该函数计算任何通用括号的值,包括回合数,种子序列(32队括号中的1和32、2和31之间的对位,等等),以及各个列的时间段。 这项挑战留给读者练习。
问题的核心是您需要遍历表的32行,但是XSLT 1.0没有提供执行此操作的实用方法。 XPath 2.0和XSLT 2.0的新功能可以帮助您解决此问题。
翻译自: https://www.ibm.com/developerworks/xml/library/x-xslt20xpath20/index.html
xpath1.0和2.0