1

我有一个这样的 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>

我尝试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>    
4

2 回答 2

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>
于 2013-04-04T19:59:25.843 回答
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>
于 2013-04-04T20:09:13.603 回答