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 输入而不会 运行 出现内存问题。
我饶有兴趣地研究了“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 输入而不会 运行 出现内存问题。