按属性对XML元素进行分组

时间:2013-01-02 11:41:06

标签: xml xslt xslt-1.0

我有一个名为filt的变量,它包含一个像这样的xml:

<filters>
  <ISP_WebItem FILTER="Farve" FILTERNAME="Transparent" UNITCODE="" />
  <ISP_WebItem FILTER="Antal" FILTERNAME="10" UNITCODE="mapper" />
  <ISP_WebItem FILTER="Indpakning" FILTERNAME="Æske" UNITCODE="" />
  <ISP_WebItem FILTER="Materiale" FILTERNAME="PP" UNITCODE="" />
  <ISP_WebItem FILTER="Bredde" FILTERNAME="35.6" UNITCODE="cm" />
  <ISP_WebItem FILTER="Farve" FILTERNAME="blue" UNITCODE="" />
  <ISP_WebItem FILTER="Dybde" FILTERNAME="5" UNITCODE="mm" />
  <ISP_WebItem FILTER="Farve" FILTERNAME="red" UNITCODE="" />
</filters>

我想通过'FILTER'属性对这些元素进行分组。 那就是我想要一个像这样的输出xml(请注意xml元素是由过滤器属性重新排列的,即所有带有FILTER的元素现在都在相邻的位置)

<filters>
  <ISP_WebItem FILTER="Farve" FILTERNAME="Transparent" UNITCODE="" />
  <ISP_WebItem FILTER="Farve" FILTERNAME="blue" UNITCODE="" />
  <ISP_WebItem FILTER="Farve" FILTERNAME="red" UNITCODE="" />
  <ISP_WebItem FILTER="Antal" FILTERNAME="10" UNITCODE="mapper" />
  <ISP_WebItem FILTER="Indpakning" FILTERNAME="Æske" UNITCODE="" />
  <ISP_WebItem FILTER="Materiale" FILTERNAME="PP" UNITCODE="" />
  <ISP_WebItem FILTER="Bredde" FILTERNAME="35.6" UNITCODE="cm" />
  <ISP_WebItem FILTER="Dybde" FILTERNAME="5" UNITCODE="mm" />
</filters>

我尝试过这样的事情:

  <xsl:variable name="grouped_filt" select="$filt//ISP_WebItem[@FILTER = preceding-sibling::*[1]/@FILTER ]"></xsl:variable>

但没有用。我无法在此找到任何错误。有人可以帮忙吗?

1 个答案:

答案 0 :(得分:1)

XSLT 1.0中的标准方法称为Muenchian Grouping

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:strip-space elements="*"/>
  <xsl:output method="xml" indent="yes" />
  <xsl:key name="itemByFilter" match="ISP_WebItem" use="@FILTER" />

  <xsl:template match="filters">
    <filters>
      <xsl:apply-templates select="ISP_WebItem[
          generate-id() = generate-id(key('itemByFilter', @FILTER)[1])]" />
    </filters>
  </xsl:template>

  <xsl:template match="ISP_WebItem">
    <xsl:copy-of select="key('itemByFilter', @FILTER)" />
  </xsl:template>
</xsl:stylesheet>

generate-id() = generate-id(key('itemByFilter', @FILTER)[1])是一种仅使用每个ISP_WebItem值选择第一个 FILTER元素的方法,并将ISP_WebItem模板应用于该元素。在模板中,我们然后复制具有相同FILTER值的所有元素。

编辑:

您说<filters>元素位于“名为filt的变量”中,而不是您直接从输入文档中匹配的内容。在这种情况下,您可以使用相同的键定义

  <xsl:key name="itemByFilter" match="ISP_WebItem" use="@FILTER" />

但您使用<xsl:template match="filters">代替<xsl:for-each select="$filt/filters">。如果filt变量是结果树片段而不是节点集 - 即它是创建为

<xsl:variable name="filt">
  <filters>
    <!-- ... -->
  </filters>
</xsl:variable>

您将需要一个扩展功能将其重新转换为节点集

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
                xmlns:exslt="http://exslt.org/common"
                exclude-result-prefixes="exslt">
  <xsl:strip-space elements="*"/>
  <xsl:output method="xml" indent="yes" />
  <xsl:key name="itemByFilter" match="ISP_WebItem" use="@FILTER" />


  <xsl:variable name="filt">
    <filters>
      <ISP_WebItem FILTER="Farve" FILTERNAME="Transparent" UNITCODE="" />
      <ISP_WebItem FILTER="Antal" FILTERNAME="10" UNITCODE="mapper" />
      <ISP_WebItem FILTER="Farve" FILTERNAME="blue" UNITCODE="" />
    </filters>
  </xsl:variable>

  <xsl:variable name="grouped_filt">
    <xsl:for-each select="exslt:node-set($filt)/filters">
      <filters>
        <xsl:apply-templates select="ISP_WebItem[
            generate-id() = generate-id(key('itemByFilter', @FILTER)[1])]" />
      </filters>
    </xsl:for-each>
  </xsl:variable>

  <xsl:template match="/">
    <!-- Demonstrate that the grouping did the right thing -->
    <xsl:copy-of select="$grouped_filt" />
  </xsl:template>

  <xsl:template match="ISP_WebItem">
    <xsl:copy-of select="key('itemByFilter', @FILTER)" />
  </xsl:template>
</xsl:stylesheet>

此样式表在任何输入文档(例如<foo/>)上运行时都会输出

<?xml version="1.0"?>
<filters>
  <ISP_WebItem FILTER="Farve" FILTERNAME="Transparent" UNITCODE=""/>
  <ISP_WebItem FILTER="Farve" FILTERNAME="blue" UNITCODE=""/>
  <ISP_WebItem FILTER="Antal" FILTERNAME="10" UNITCODE="mapper"/>
</filters>