XSLT将结果分成3组,而忽略输入树结构

时间:2018-08-09 19:11:21

标签: xml xslt xslt-grouping

我感兴趣地研究了“ 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>

谢谢!

1 个答案:

答案 0 :(得分:1)

对于XSLT 1,您需要将选择更改为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,您可以根据需要选择并分组:

<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(显示在https://stackoverflow.com/a/7320527/252228中),那样,该解决方案更适合使用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输入,而不会遇到内存问题。