如何平展相邻相同节点的组?

  • 本文关键字:节点 何平 xslt xslt-1.0
  • 更新时间 :
  • 英文 :


假设我有以下元素序列:

<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>

解释

  1. 标识规则(模板)按原样复制到每个节点的输出。我们还给它起了一个名字,这样我们不仅可以在应用模板时使用它,还可以直接调用它——就像我们在步骤 4 中所做的那样。下面。

  2. 与尚未在<group>内的任何<e>元素匹配的模板。它没有正文 - 我们只想在下一个模板中处理这些元素 - 而不是在标识模板选择它执行时。

  3. 与"未覆盖"组中的第一个元素的任何<e>元素匹配的模板。这使用了Muenchian分组方法背后的思想 - 如果你不熟悉它,请好好学习它 - 你不会后悔。此模板为整个组生成一个包装器元素(称为"组生成"),并且在此包装器中以"inGroup"模式将模板应用于组中的所有<e>- 这些模板只是简单地复制。

  4. 模式"inGroup"中的模板只是委托给标识规则来复制所选节点。因此,任何匹配的节点都将按原样复制。在这里,如果我们决定,我们可以执行任何其他所需的"组内"处理。

相关内容

  • 没有找到相关文章

最新更新