XSLT 拆分结果为 3 组,同时忽略输入树结构

XSLT split result in groups of 3 while ignoring input tree structure

我饶有兴趣地研究了“XSLT split result in groups of 3”的解法。引用示例中的元素都在一个节点下。当我扩展示例数据以包含 的 2 个分支时,如下所示,

<Root>
    <nums>
        <num>01</num>
        <num>02</num>
        <num>03</num>
        <num>04</num>
    </nums>
    <nums>
        <num>11</num>
        <num>12</num>
        <num>13</num>
        <num>14</num>
    </nums>
</Root> 

使用修改后的 xslt 如下:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="pGroupSize" select="3"/>

    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="/Root/*">
        <nums>
            <xsl:apply-templates select=
                "num[position() mod $pGroupSize = 1]"/>
        </nums>
    </xsl:template>

    <xsl:template match="num">
        <group>
            <xsl:copy-of select=
                ".|following-sibling::*
                [not(position() > $pGroupSize -1)]"/>
        </group>
    </xsl:template>
</xsl:stylesheet>

输出结果如下:

<Root>
   <nums>
      <group>
         <num>01</num>
         <num>02</num>
         <num>03</num>
      </group>
      <group>
         <num>04</num>
      </group>
   </nums>
   <nums>
      <group>
         <num>11</num>
         <num>12</num>
         <num>13</num>
      </group>
      <group>
         <num>14</num>
      </group>
   </nums>
</Root>

但是,假设所需的输出应采用以下形式,如下所示。如何做到这一点?

<Root>
   <nums>
      <group>
         <num>01</num>
         <num>02</num>
         <num>03</num>
      </group>
      <group>
         <num>04</num>
         <num>11</num>
         <num>12</num>
      </group>
      <group>
         <num>13</num>
         <num>14</num>
      </group>
   </nums>
</Root>

谢谢!

对于 XSLT 1,您需要将 selection 更改为 descendant::num,将 following-sibling::* 更改为 following::num:

<xsl:template match="/Root">
    <nums>
        <xsl:apply-templates select=
            "descendant::num[position() mod $pGroupSize = 1]"/>
    </nums>
</xsl:template>

<xsl:template match="num">
    <group>
        <xsl:copy-of select=
            ".|following::num
            [not(position() > $pGroupSize -1)]"/>
    </group>
</xsl:template>

完整示例https://xsltfiddle.liberty-development.net/jyH9rMA

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="pGroupSize" select="3"/>

    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="/Root">
        <nums>
            <xsl:apply-templates select=
                "descendant::num[position() mod $pGroupSize = 1]"/>
        </nums>
    </xsl:template>

    <xsl:template match="num">
        <group>
            <xsl:copy-of select=
                ".|following::num
                [not(position() > $pGroupSize -1)]"/>
        </group>
    </xsl:template>
</xsl:stylesheet>

使用 XSLT 2 或 3 for-each-group 您可以简单地 select 并根据需要分组:

<xsl:template match="/Root">
    <xsl:copy>
        <nums>
            <xsl:for-each-group select="nums/num" group-adjacent="(position() - 1) idiv $pGroupSize">
                <group>
                    <xsl:sequence select="current-group()"/>
                </group>
            </xsl:for-each-group>
        </nums>            
    </xsl:copy>
</xsl:template>

https://xsltfiddle.liberty-development.net/jyH9rMA/2:

<xsl:stylesheet version="3.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="pGroupSize" select="3"/>

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:template match="/Root">
        <xsl:copy>
            <nums>
                <xsl:for-each-group select="nums/num" group-adjacent="(position() - 1) idiv $pGroupSize">
                    <group>
                        <xsl:sequence select="current-group()"/>
                    </group>
                </xsl:for-each-group>
            </nums>            
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

我使用 group-adjacent 而不是 group-by(如 所示),因为这样解决方案更适合使用 Saxon 9.8 EE 或 Exselt

进行流式传输
<xsl:stylesheet version="3.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="pGroupSize" select="3"/>

    <xsl:mode on-no-match="shallow-copy" streamable="yes"/>

    <xsl:template match="/Root">
        <xsl:copy>
            <nums>
                <xsl:for-each-group select="nums/num" group-adjacent="(position() - 1) idiv $pGroupSize">
                    <group>
                        <xsl:sequence select="current-group()"/>
                    </group>
                </xsl:for-each-group>
            </nums>        
        </xsl:copy>

    </xsl:template>

</xsl:stylesheet>

因此您可以将样式表用于大量 XML 输入而不会 运行 出现内存问题。