假设我有以下元素序列:
<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']
或一些这样的元素,但这假设某个父元素并且它返回单个节点集,而我需要两个。
解决此问题的一种方法是使用所谓的同级递归:
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>
无递归。使用键(明基安分组):
<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>
在提供的源 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 中所做的那样。下面。
与尚未在
<group>
内的任何<e>
元素匹配的模板。它没有正文 - 我们只想在下一个模板中处理这些元素 - 而不是在标识模板选择它执行时。与"未覆盖"组中的第一个元素的任何
<e>
元素匹配的模板。这使用了Muenchian分组方法背后的思想 - 如果你不熟悉它,请好好学习它 - 你不会后悔。此模板为整个组生成一个包装器元素(称为"组生成"),并且在此包装器中以"inGroup"模式将模板应用于组中的所有<e>
- 这些模板只是简单地复制。模式"inGroup"中的模板只是委托给标识规则来复制所选节点。因此,任何匹配的节点都将按原样复制。在这里,如果我们决定,我们可以执行任何其他所需的"组内"处理。