如何使用XSLT对值进行排序?

时间:2011-09-27 19:47:15

标签: xml xslt xml-parsing xslt-2.0

我有一个与此类似的XML:

<Test>
  <grapes>
     <a>TypeA</a>
     <b>value1</b>
  </grapes>
  <oranges>
     <a>TypeB</a>
     <b>value2</b>
  </oranges>
  <apples>
    <a>TypeA</a>
     <b>value3</b>
  </apples>
</Test>

其中值是唯一的但类型可能相同。

我正在尝试对其进行排序,以使输出类似于:

<group type="TypeA">
  <value v="value1" />
  <value v="value3" />
</group>
<group type="TypeB">
  <value v="value2" />
</group>

我很难确保这些组在输出中是唯一的,而且值在正确的组中。

我的XSL应该如何构建?

3 个答案:

答案 0 :(得分:2)

这是一个更简单的解决方案(完全“推送式”,没有<xsl:for-each>,没有嵌套,没有<xsl:variable>,没有current(),没有{ {1}},没有轴):

//

将此转换应用于提供的XML文档

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

 <xsl:key name="kGoodsByType" match="/*/*" use="a"/>

 <xsl:template match=
  "/*/*[generate-id()
       =
        generate-id(key('kGoodsByType', a)[1])
       ]
  ">
     <group type="{a}">
       <xsl:apply-templates select="key('kGoodsByType', a)/b"/>
     </group>
 </xsl:template>

 <xsl:template match="b">
  <value v="{.}"/>
 </xsl:template>

 <xsl:template match="*/* | text()" priority="-1"/>
</xsl:stylesheet>

产生了想要的正确结果

<Test>
    <grapes>
        <a>TypeA</a>
        <b>value1</b>
    </grapes>
    <oranges>
        <a>TypeB</a>
        <b>value2</b>
    </oranges>
    <apples>
        <a>TypeA</a>
        <b>value3</b>
    </apples>
</Test>

解释<group type="TypeA"> <value v="value1"/> <value v="value3"/> </group> <group type="TypeB"> <value v="value2"/> </group> /*/*使用a个孩子的字符串值作为关键字。

<强> II。 XSLT 2.0解决方案:

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

 <xsl:template match="/*">
  <xsl:for-each-group select="*/a" group-by=".">
   <group type="{current-grouping-key()}">
     <xsl:sequence select="current-group()/../b"/>
   </group>
  </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>

在同一个XML文档(上面)上执行此转换时,会生成相同的正确结果

<group type="TypeA">
   <b>value1</b>
   <b>value3</b>
</group>
<group type="TypeB">
   <b>value2</b>
</group>

<强>解释

  1. <xsl:for-each-group>

  2. current-group()

  3. current-grouping-key()

答案 1 :(得分:1)

XSLT 1.0:

首先,使用muenchian方法为类型创建唯一的组。谷歌它找出它是什么。然后,只需迭代它们并打印出你想要的东西,你想要它:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="types" match="a" use="text()"/>
<xsl:template match="/">
<result>
  <xsl:for-each select="//a[generate-id(.) = generate-id(key('types', text())[1])]">
    <group type="{current()/text()}">
      <xsl:for-each select="//a[text() = current()/text()]">
        <xsl:variable name="values" select="following-sibling::b | preceding-sibling::b"/>
        <xsl:for-each select="$values">
          <value v="{current()}"/>
        </xsl:for-each>
      </xsl:for-each>
    </group>
  </xsl:for-each>
</result>
</xsl:template>
</xsl:stylesheet>

您会发现输出与您期望的相同。

答案 2 :(得分:0)

我的建议与FailedDev略有不同:

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

  <xsl:template match="/Test">
    <root>
      <xsl:for-each select="*[a != following::a]">
        <xsl:sort select="a" data-type="text" />
        <group type="{a}">
          <xsl:for-each select="/Test/*[a = current()/a]">
            <xsl:sort select="b" data-type="text" />
            <value v="{b}" />
          </xsl:for-each>
        </group>
      </xsl:for-each>
    </root>
  </xsl:template>
</xsl:stylesheet>

外部

<xsl:for-each select="*[a != following::a]" />

选择所有唯一类型,即。 TypeA TypeB 。 在

<xsl:sort select="a" data-type="text" />

根据名称对这些进行排序,确保 TypeA 出现在 TypeB 之上。 内在的

<xsl:for-each select="/Test/*[a = current()/a]" />

为每种类型选择唯一值列表,即。对于 TypeA ,提取值 value1 value3 。同样,结果列表在 value3 之前排序为列出 value1