1

我经常需要使用 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's libxslt,所以如果需要,我确实可以exslt:node-set()使用该功能。

4

2 回答 2

3

这里是 muenchian 分组对您的示例的改编。有关更多信息,请查看此处。如果您了解它的工作原理并尝试使其适应不断变化的分组问题,它就会变得非常方便。

<?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>
于 2013-05-29T20:06:08.860 回答
0

总的来说:阅读 Muenchian 分组。

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

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

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

你怎么能那样做?

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

<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给定前缀值的元素中$prefix,我们希望输入中z的每个元素都有一个x共享该前缀的元素。因此,完成这项工作的一种简单方法是仅在该组y元素上调用应用模板。为了避免干扰匹配x元素和生成y元素的模板,给它一个模式。对 apply-templates 的调用可能如下所示:

<xsl:apply-templates mode="z-production"
  select="//x[substring-before(.,'/') = $prefix]"/>

现在在 z-production 模式下为 x 元素编写模板。

最后,更改样式表以生成逗号和空格而不是z元素。

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

于 2013-05-29T16:05:42.767 回答