如何在XSLT 1.0中聚合节点?

时间:2013-05-29 14:49:41

标签: xml xslt xslt-1.0 aggregation

我经常需要使用XSLT 1.0聚合一系列节点,但我总是很难找到一个干净的解决方案。

这是一个典型的例子;

输入

<x>Foo/Red</x>
<x>Foo/Green</x>
<x>Foo/Blue</x>
<x>Bar/Hello</x>
<x>Bar/World</x>

所需的输出

<y s="Foo">Red, Green, Blue</y>
<y s="Bar">Hello, World</y>

我总是把这种问题搞得一团糟。上面有一个优雅的XSLT 1.0解决方案吗?

我正在使用PHP的{​​{1}}所以我确实可以使用libxslt功能。

2 个答案:

答案 0 :(得分:3)

这里是对你的例子的muenchian分组的改编。有关详细信息,请参阅here。 如果你已经了解它是如何工作的,并试图使其适应不断变化的分组问题,那么它就变得非常方便了。

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:key name="kXprev" match="x" use="substring-before(text(),'/')"/>

    <xsl:template name="y">
        <xsl:param name="s" />
        <y s="{$s}">
            <xsl:for-each select="key('kXprev', $s)">
                <xsl:if test="position()>1" >
                    <xsl:text> </xsl:text>
                </xsl:if>
                <xsl:value-of select="substring-after(text(),'/')"/>
            </xsl:for-each>
        </y>

    </xsl:template>
    <xsl:template match="/*">
        <out>
            <xsl:for-each select="x[count( . | key('kXprev',substring-before(text(),'/') )[1] ) =1]" >
                <xsl:call-template name="y">
                    <xsl:with-param name="s" select="substring-before(text(),'/') "/>
                </xsl:call-template>
            </xsl:for-each>
        </out>
    </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:0)

总的来说:阅读Muenchian分组。

对于这个特定问题(但也是一般有用的建议):将其分解成更小的部分。

分解它的一种方法:您希望为输入中y元素的表达式substring-before(。,'/')的每个不同值生成一个x元素。或者可能是每次出现x,其中子字符串与其前一个兄弟的相应子字符串不同。首先编写一个样式表,它只生成正确数量的y元素,并具有s属性的适当值。所以输出是

<y s="Foo"/>
<y s="Bar"/>

你怎么能这样做?

完成后,您需要为y元素提供内容:对于共享相同前缀的输入中的每个x,打印出字符串值。在最终版本中,您将需要逗号和空格,但正确使用它们会涉及一些繁琐的细节,因此请编写样式表的下一个版本以生成z元素序列作为y的子元素:< / p>

<y s="Foo"><z>Red</z><z>Green</z><z>Blue</z></y>
<y s="Bar"><z>Hello</z><z>World</z></y>

z元素需要出现在y元素中。因此,生成y元素的模板将更改为:

<xsl:template match="...">
  <!--* old code here ... *-->
  <xsl:element name="y">
    <xsl:attribute name="s">
      <xsl:value-of select="..."/>
    </xsl:attribute>

    <!--* code to produce 'z' elements goes here *-->

  </xsl:element>
</xsl:template>

生成z元素的代码应该是什么样的?在给定前缀值y的{​​{1}}元素内,我们希望输入中每个$prefix的一个z元素共享该前缀。因此,一种简单的方法就是只对那组x元素调用apply-templates。为避免干扰匹配y元素并生成x元素的模板,请为其指定模式。对apply-templates的调用可能如下所示:

y

现在在模式z-production中编写x元素的模板。

最后,更改样式表以生成逗号和空格,而不是<xsl:apply-templates mode="z-production" select="//x[substring-before(.,'/') = $prefix]"/> 元素。

还有其他方法可以将问题分解成更小的部分;有时将树从兄弟姐妹走到兄弟姐妹而不是从父母走到兄弟姐妹是有帮助的。