假设我具有以下元素序列:
<outer>
<e>…</e> <!-- Adjacent <e> should be grouped if they aren’t yet. -->
<e>…</e>
<group>
<e>…</e>
<e>…</e>
</group>
<e>…</e>
<e>…</e>
</outer>
我想合并尚未分组的元素<e>
,即输出将是
<outer>
<group-foo> <!-- Grouped elements. -->
<e>…</e>
<e>…</e>
</group-foo>
<group-bar>
<e>…</e>
<e>…</e>
</group-bar>
<group-foo>
<e>…</e>
<e>…</e>
</group-foo>
</outer>
我只是不太清楚如何选择相邻元素的一个组(一个节点集);最接近的主意是选择//e[name(parent::*) = 'outer']
或类似的东西,但是它假定某个父元素并且它返回一个节点集,而我需要两个。
答案 0 :(得分:1)
解决此问题的一种方法是使用所谓的同级递归:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/outer">
<xsl:copy>
<xsl:apply-templates select="e[not(preceding-sibling::*[1][self::e])] | group"/>
</xsl:copy>
</xsl:template>
<xsl:template match="e">
<group-foo>
<xsl:copy-of select="."/>
<!-- immediate sibling in the same group -->
<xsl:apply-templates select="following-sibling::*[1][self::e]" mode="collect" />
</group-foo>
</xsl:template>
<xsl:template match="e" mode="collect">
<xsl:copy-of select="."/>
<!-- immediate sibling in the same group -->
<xsl:apply-templates select="following-sibling::*[1][self::e]" mode="collect" />
</xsl:template>
<xsl:template match="group">
<group-bar>
<xsl:copy-of select="*"/>
</group-bar>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:0)
无递归。使用键( Muenchian grouping ):
<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:key name="kE-ByPrecedingNonE" match="e[not(name(..) = 'group')]"
use="generate-id(preceding-sibling::*[not(self::e)][1])"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="e[not(name(..) = 'group')]"/>
<xsl:template match=
"e[generate-id()
= generate-id(key('kE-ByPrecedingNonE',
generate-id(preceding-sibling::*[not(self::e)][1])
)[1]
)]">
<group-generated>
<xsl:apply-templates select=
"key('kE-ByPrecedingNonE',
generate-id(preceding-sibling::*[not(self::e)][1])
)" mode="inGroup"/>
</group-generated>
</xsl:template>
<xsl:template match="node()" mode="inGroup">
<xsl:call-template name="identity"/>
</xsl:template>
</xsl:stylesheet>
将此转换应用于提供的source-xml文档时:
<outer>
<e>…</e>
<e>…</e>
<group>
<e>…</e>
<e>…</e>
</group>
<e>…</e>
<e>…</e>
</outer>
产生所需的正确结果:
<outer>
<group-generated>
<e>…</e>
<e>…</e>
</group-generated>
<group>
<e>…</e>
<e>…</e>
</group>
<group-generated>
<e>…</e>
<e>…</e>
</group-generated>
</outer>
说明:
身份规则(模板)将每个节点原样复制到输出中。我们还给它起了一个名称,这样我们不仅可以在应用模板时使用它,还可以直接调用它-就像下面的步骤4一样。
与<e>
内尚未存在的任何<group>
元素匹配的模板。它没有内容-我们只希望在下一个模板中处理这些元素-而不是在身份模板选择要执行的元素时。
与任何<e>
元素匹配的模板,该元素是“未覆盖”组中的第一个元素。这使用了Muenchian分组方法背后的想法-如果您不熟悉它,请学习它-您不会后悔的。该模板为整个组生成一个包装器元素(称为“组生成的”),并且在该包装器内部以“ inGroup”模式将模板应用于该组中的所有<e>
,只需将它们复制即可。
处于“ inGroup”模式下的模板仅委托给身份规则来复制所选节点。因此,任何匹配的节点都将按原样复制。在这里,如果我们决定,我们可以执行任何其他必需的“ inGroup”处理。