XSL按嵌套参数分组

时间:2013-04-04 19:29:51

标签: xml xslt xslt-2.0

我有一个像这样的XML文件

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

我需要以另一种方式对其进行分组:

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

我尝试使用来自XSLT 2.0的for-each-group,但它一点都不好:我不知道如何通过嵌套参数处理分组,所以我的.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>    

2 个答案:

答案 0 :(得分: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 :(得分:1)

如果你只限于XSLT 1.0,那么有几种方法可以做到:没有一个是整洁的。这个查找所有没有同一国家的兄弟姐妹的<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,因此可以使用<fruit>选择具有<country>元素相同值的所有key('fruit-by-country', 'Morocco')元素。模板使用密钥检查当前<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>