2013-04-04 66 views
1

我有这样XSL分组通过嵌套参数

<fruits> 
    <fruit> 
    <name>banana</name> 
    <country>Morocco</country> 
    </fruit> 
    <fruit> 
    <name>orange</name> 
    <country>Morocco</country> 
    </fruit> 
    <fruit> 
    <name>grape</name> 
    <country>Egypt</country> 
    </fruit> 
</fruits> 

一个XML文件,我需要将其组以另一种方式:

<fruits> 
    <country name="Morocco"> 
    <fruit> 
     <name>banana</name> 
    </fruit> 
    <fruit> 
     <name>orange</name> 
    </fruit> 
    </country> 
    <country name="Egypt"> 
    <fruit> 
     <name>grape</name> 
    </fruit> 
    </country> 
</fruits> 

我试图使它与for-each-group从XSLT 2.0,但它并不好:我不知道如何通过嵌套参数来处理分组,所以我的.xsl文件没有任何好处。

<?xml version="1.0"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> 

    <xsl:output method="xml" indent="yes"/> 

    <xsl:for-each-group select="fruits/fruit" group-by="fruits/fruit/country"> 
    <country name="{country}"> 
     <xsl:for-each select="current-group()"> 
     <fruit> 
      <name> '{name}'/</name> 
     </fruit> 
     </xsl:for-each> 
    </country> 
    </xsl:for-each-group> 

</xsl:stylesheet>  
+0

你接近 - 在'组by'表达是相对于'select'ed节点的XPath,所以你只需要'组通过=“国家”' – 2013-04-04 20:12:04

回答

1

如何:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> 
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/> 

    <xsl:template match="/*"> 
    <xsl:copy> 
     <xsl:for-each-group select="fruit" group-by="country"> 
     <country name="{country}"> 
      <xsl:for-each select="current-group()"> 
      <fruit> 
       <name> 
       <xsl:value-of select="name" /> 
       </name> 
      </fruit> 
      </xsl:for-each> 
     </country> 
     </xsl:for-each-group> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

或稍微更简洁的方法:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> 
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="@* | node()"> 
    <xsl:copy> 
     <xsl:apply-templates select="@* | node()" /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="/*"> 
    <xsl:copy> 
     <xsl:for-each-group select="fruit" group-by="country"> 
     <country name="{country}"> 
      <xsl:apply-templates select="current-group()" /> 
     </country> 
     </xsl:for-each-group> 
    </xsl:copy> 
    </xsl:template> 
    <xsl:template match="fruit/country" /> 

</xsl:stylesheet> 

任一个,当你的样品输入运行,生产:

<fruits> 
    <country name="Morocco"> 
     <fruit> 
     <name>banana</name> 
     </fruit> 
     <fruit> 
     <name>orange</name> 
     </fruit> 
    </country> 
    <country name="Egypt"> 
     <fruit> 
     <name>grape</name> 
     </fruit> 
    </country> 
</fruits> 
1

如果你仅限于XSLT 1.0,那么就有一个fe w的方式来做到这一点:他们没有一个整洁。这一个查找所有<fruit>元素没有在同一个国家的兄弟姐妹。然后,它复制<country>元素,然后为其自身创建一个新的<fruit>节点,然后每个节点跟随同一个国家/地区的兄弟节点。

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="1.0"> 

    <xsl:output indent="yes"/> 

    <xsl:template match="/fruits"> 
    <xsl:copy> 
     <xsl:apply-templates select="*"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="fruit"> 
    <xsl:if test="not(country = preceding-sibling::fruit/country)"> 
     <country> 
     <xsl:attribute name="name"> 
      <xsl:value-of select="country"/> 
     </xsl:attribute> 
     <xsl:for-each select="../fruit[country=current()/country]"> 
      <fruit> 
      <xsl:copy-of select="name" /> 
      </fruit> 
     </xsl:for-each> 
     </country> 
    </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 

输出

<?xml version="1.0" encoding="utf-8"?> 
<fruits> 
    <country name="Morocco"> 
     <fruit> 
     <name>banana</name> 
     </fruit> 
     <fruit> 
     <name>orange</name> 
     </fruit> 
    </country> 
    <country name="Egypt"> 
     <fruit> 
     <name>grape</name> 
     </fruit> 
    </country> 
</fruits> 

Muenchian方法加速通过使用XSLT的key设施这个查询,并且可以是相当大的与数据集是有用的。该替代解决方案声明密钥fruit-by-country,以便可以使用例如key('fruit-by-country', 'Morocco')来选择具有与<country>元素相同值的所有<fruit>元素。该模板使用密钥来检查当前<fruit>是否是第一个具有此值的<country>,并且还选择同一组中的所有水果,以便它们可以一起显示。输出与之前的变换相同。

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="1.0"> 

    <xsl:output indent="yes"/> 

    <xsl:key name="fruit-by-country" match="fruit" use="country" /> 

    <xsl:template match="/fruits"> 
    <xsl:copy> 
     <xsl:apply-templates select="*"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="fruit"> 
    <xsl:if test="generate-id() = generate-id(key('fruit-by-country', country)[1])"> 
     <country> 
     <xsl:attribute name="name"> 
      <xsl:value-of select="country"/> 
     </xsl:attribute> 
     <xsl:for-each select="key('fruit-by-country', country)"> 
      <fruit> 
      <xsl:copy-of select="name" /> 
      </fruit> 
     </xsl:for-each> 
     </country> 
    </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 
+0

即使在XSLT 1.0, Muenchian分组远比'前同胞'更好。 – JLRishe 2013-04-04 20:25:06

+0

@JLRishe:我同意它更有效率,但也是(更加)不透明和难以理解的。所以这取决于你的意思*优选*。你已经明白了你的观点,但是倒退表示我的答案*没有用*。你真的准备好说吗? – Borodin 2013-04-04 20:27:32

+0

这只是我的看法,但我认为将“前同胞”分组是一种糟糕的做法,即使它比较容易理解。你的回答也是不正确的。请仔细比较您的输出与OP的期望输出。 – JLRishe 2013-04-04 20:39:21