我假设您想要深入描述 xsl:for-each-group 以及如何使用它。如果这不是您要的,请告诉我。
XSLT 2.0 中的新指令采用一组项目并将它们分组。这组项目称为“人口”,而组仅称为组。该指令依次处理每个组。
xsl:for-each-group 指令的可能属性包括:
- 选择
- 通过...分组
- 组相邻
- 组开始
- 组结尾与
- 整理
@select 是强制性的。其他是可选的。它可以采用任意数量的 xsl:sort 子级(但它们必须先出现),然后是一个序列构造函数。“序列构造函数”是指所有在模板等内部发出序列的类型指令的术语。
@选择
select 属性指定一个 XPATH 表达式,它计算要分组的总体。
@通过...分组
group-by 属性指定一个 XPATH 表达式,当分组类型是按公共值时,您可以使用该表达式。总体中评估为与另一个相同的分组值的每个项目都与另一个在同一组中。
XSLT 1.0 Muenchian 分组在分组类型是按共同值分组时并不太难。有两种更常见的分组形式:按相似值对相邻项进行分组;并将相邻的一组项目分组,这些项目的组要么在末尾划界,要么在开始时通过某些测试划定。虽然这两种形式的分组在 Muenchian 中仍然是可能的,但它变得相对复杂。由于使用了同级轴(不管你怎么拼写!),这些类型的 Muenchian 在规模上的效率也会降低。
我想到的 XSLT 2.0 的另一个优点是 Muenchian 仅适用于节点集,而 xsl:for-each-group 的应用范围更广,因为它适用于一系列项目,而不仅仅是节点。
@group-by 表达式的结果将是一系列项目。这个序列被原子化和重复数据删除。被测试的总体项目将是每个值的一个组的成员。这是一个奇怪的结果,使用@group-by,并且 item 可能是多个组的成员,甚至可能没有。虽然我怀疑您可以在 XSLT 2.0 中做的任何事情,但通过一些曲折的路径,您可以在 XSLT 1.0 中做,将一个项目分成两组的能力在 XSLT 1.0 Muenchian 中会很安静。
@group-相邻
属性 group-by、group-adjacent、group-starting-with 和 group-ending-with 是互斥的,因为它们指定了不同类型的分组。具有公共值且在总体中相邻的项目被分组在一起。与@group-by 不同,@group-adjacent 在原子化之后必须评估为单个原子值。
组开始
与 select、group-adjacent 和 group-by 不同,此属性不指定 XPATH 选择表达式,而是指定模式,就像 xsl:template/@match 指定模式而不是选择一样。如果总体中的一个项目通过了模式测试或者是总体中的第一个项目,那么它将开始一个新的组。否则,该项目从前一个项目继续该组。
Martin 提到了规范示例 (w3.org/TR/xslt20/#grouping-example)。从该参考资料中,我将复制题为“通过初始元素识别组”的示例,但稍微改变它以强调关于总体初始项的要点。
所以这是我们的输入文档(从 w3 规范复制。包含孤立行是我的)...
<body>
<p>This is an orphaned paragraph.</p>
<h2>Introduction</h2>
<p>XSLT is used to write stylesheets.</p>
<p>XQuery is used to query XML databases.</p>
<h2>What is a stylesheet?</h2>
<p>A stylesheet is an XML document used to define a transformation.</p>
<p>Stylesheets may be written in XSLT.</p>
<p>XSLT 2.0 introduces new grouping constructs.</p>
</body>
...我们想要做的是将组定义为以 h2 开头的节点,并包括所有后续 p 直到下一个 h2。w3给出的示例解决方案是使用@group-starting-with ...
<xsl:template match="body">
<chapter>
<xsl:for-each-group select="*" group-starting-with="h2" >
<section title="{self::h2}">
<xsl:for-each select="current-group()[self::p]">
<para><xsl:value-of select="."/></para>
</xsl:for-each>
</section>
</xsl:for-each-group>
</chapter>
</xsl:template>
在规范示例中,当输入不包含孤立行时,这会产生所需的结果......
<chapter>
<section title="Introduction">
<para>XSLT is used to write stylesheets.</para>
<para>XQuery is used to query XML databases.</para>
</section>
<section title="What is a stylesheet?">
<para>A stylesheet is an XML document used to define a transformation.</para>
<para>Stylesheets may be written in XSLT.</para>
<para>XSLT 2.0 introduces new grouping constructs.</para>
</section>
</chapter>
虽然在我们的特殊情况下,我们得到了......
<chapter>
<section title="">
<para>This is an orphaned paragraph.</para>
</section>
<section title="Introduction">
<para>XSLT is used to write stylesheets.</para>
<para>XQuery is used to query XML databases.</para>
</section>
<section title="What is a stylesheet?">
<para>A stylesheet is an XML document used to define a transformation.</para>
<para>Stylesheets may be written in XSLT.</para>
<para>XSLT 2.0 introduces new grouping constructs.</para>
</section>
</chapter>
如果不希望孤立行的初始部分,则有简单的解决方案。我现在不会进入他们。我的观点只是要强调一个事实,即@group-starting-with 产生的第一组可以是“孤儿”组。“孤儿”是指头节点不符合指定模式的组。
@collation
collation 属性指定一个排序规则 URI 并标识用于比较字符串是否相等的排序规则。
当前组()
在 xsl:for-each-group 中,current-group() 函数将当前组作为项目序列进行处理。
当前分组键()
在 xsl:for-each-group 中,current-group() 函数返回当前组键。我不确定,但我相信这只能是原子类型。也不确定,但我相信这个功能只适用于@group-by 和@group-adjacent 类型的分组。
@group-by 与 @group-adjacent
在某些情况下,您可以在具有相同功能结果的这两种排序类型之间进行选择。在这种情况下,@group-adjacent 优先于 @group-by,因为它的处理效率可能更高。
模式与选择
一些 XSLT 2.0 指令属性包含选择表达式。Michael Kay 将这些称为“XPath 表达式”。就个人而言,当与模式并列时,我觉得更好的描述是“选择表达”。其他属性包含模式或“匹配表达式”。虽然这两者都包含相同的语法,但它们是非常不同的野兽。两者的相似性常常使 XSLT 初学者认为 xsl:template/@match 不是一种模式,而是一种选择表达式。结果是初学者对模板序列构造函数中的 position() 函数的值产生了很多困惑。如前所述,在 xsl:for-each-group 中,@select、@group-by 和 @group-adjacent 是选择表达式,但 @group-starting-with 和 @group-ending-with 是模式。所以这里有区别:
- 选择表达式就像一个函数。输入是上下文文档、上下文序列、上下文项、上下文位置,当然还有实际表达式。输出是一系列项目。根据实际使用的位置,这可能成为下一个上下文序列。默认轴是 child:: 。
- 与 select 表达式不同,模式的默认轴是 self:: 。模式也像一个函数。它的输入和以前一样,它的输出不是一个序列,而是一个布尔值。正在测试某些项目以查看它是否与模式匹配。被测试的项目成为上下文项目。匹配表达式被临时评估,因为它是一个选择表达式。然后测试返回的序列以查看上下文项是否为成员。然后丢弃返回的序列。如果它是成员,则结果为真或“匹配”,否则为假。