统一处理分组的兄弟元素

Uniformly process grouped sibling elements

给定以下 XML 文档

<root>
  <a pos="0" total="2"/>
  <a pos="1" total="2"/>

  <a pos="0" total="3"/>
  <a pos="1" total="3"/>
  <a pos="2" total="3"/>

  <a pos="0" total="4"/>
  <a pos="1" total="4"/>
  <a pos="2" total="4"/>
  <a pos="3" total="4"/>
</root>

我需要将其翻译成

<root>
  <group>
    <a pos="0" total="2"/>
    <a pos="1" total="2"/>
  </group>
  <group>
    <a pos="0" total="3"/>
    <a pos="1" total="3"/>
    <a pos="2" total="3"/>
  </group>
  <group>
    <a pos="0" total="4"/>
    <a pos="1" total="4"/>
    <a pos="2" total="4"/>
    <a pos="3" total="4"/>
  </group>
</root>

使用 XSLT 1.0 样式表。

即文档中具有0@pos属性的每个<a>元素 隐式启动一个由它和 <a> 元素之后的 @total-1 组成的组。换句话说,@pos 表示 @total 相邻元素组中元素的从 0 开始的索引(位置)。

我想出了以下样式表,它有效:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes" />

    <xsl:template match="/">
        <xsl:apply-templates select="root" />
    </xsl:template>

    <xsl:template match="root">
        <xsl:apply-templates select="a[@pos=0]" mode="leader"/>
    </xsl:template>

    <xsl:template match="a" mode="leader">
        <group>
            <xsl:apply-templates select="." />
            <xsl:apply-templates select="following-sibling::a[position() &lt;= current()/@total - 1]" />
        </group>
    </xsl:template>

    <xsl:template match="a">
         <xsl:copy-of select="." />
    </xsl:template>

</xsl:stylesheet>

我的解决方案存在的问题是它使那些 a[@pos=0] 元素成为 "special":要进一步处理预期组中的每个 <a> 元素,我必须单独应用适当的模板首先添加到 "group leader" 元素,然后添加到组中的其余元素。

换句话说,我非常想拥有类似(不正确)的东西

    <xsl:template match="a" mode="leader">
        <group>
            <xsl:apply-templates select=". and following-sibling::a[position() &lt;= current()/@total - 1]" />
        </group>
    </xsl:template>

这将一次性将我的 <xsl:template match="a"> 模板应用于组中的所有元素。 (改写我试图在 select 表达式中拼写的内容:"select the context element and its following sibling elements matching …"。)

有没有一种方法可以在 XSLT 1.0 中获得我想要的东西,而无需诉诸变量和 exslt:node-set() 之类的技巧?可能有比我想出的更好的方法来根据元素计数进行这种分组(这本质上使每个组中的第一个元素变得特殊)?


我承认这个问题的标题很薄弱,但我没能想出一个简洁的标题来正确反映我的问题的本质。

你可以这样做:

<xsl:apply-templates select=". | following-sibling::a[position() &lt;= current()/@total - 1]" />

P.S。使用变量或 node-set() 函数不符合 "hack".