XSLT排序问题:前N个多个节点具有相等的值

时间:2012-12-01 19:06:23

标签: xslt sorting

我需要在xml树中获得前N个值,或者更好地说,我需要获得大于或等于第N个值的值,因此包括“ex-aequos”。我开始的xml就是这个

<?xml version="1.0" encoding="UTF-8"?>
<results>
    <node type="prov" id="1" name="GLD">
        <node type="gem" id ="2" name="Wageningen" value="300" />
        <node type="gem" id ="3" name="Arnhem" value="500" />
        <node type="gem" id ="4" name="Nijmegen" value="80" />
        <node type="gem" id ="5" name="Beekbergen" value="40" />
        <node type="gem" id ="6" name="Apeldoorn" value="3000" />
        <node type="gem" id ="7" name="Rhenen" value="20" />
        <node type="gem" id ="8" name="Bennekom" value="750" />
        <node type="gem" id ="9" name="Velp" value="500" />
        <node type="gem" id ="10" name="Ede" value="250" />
    </node>
    <node type="prov" id="11" name="LI">
        <node type="gem" id ="12" name="Maastricht" value="1300" />
        <node type="gem" id ="13" name="Heerlen" value="5010" />
        <node type="gem" id ="14" name="Venlo" value="1300" />
        <node type="gem" id ="15" name="Sittard" value="240" />
        <node type="gem" id ="16" name="Roermond" value="100" />
        <node type="gem" id ="17" name="Valkenburg" value="120" />
        <node type="gem" id ="18" name="Geleen" value="1750" />
        <node type="gem" id ="19" name="Venray" value="1300" />
        <node type="gem" id ="20" name="Beek" value="850" />
    </node>
</results>

以此结束,如果N = 3

<?xml version="1.0" encoding="UTF-8"?>
<results>
    <provincie id="1" name="GLD">
        <gemeente id="6" name="Apeldoorn" value="3000"/>
        <gemeente id="8" name="Bennekom" value="750"/>
        <gemeente id="3" name="Arnhem" value="500"/>
        <gemeente id="9" name="Velp" value="500"/>
    </provincie>
    <provincie id="11" name="LI">
        <gemeente id="13" name="Heerlen" value="5010"/>
        <gemeente id="18" name="Geleen" value="1750"/>
        <gemeente id="12" name="Maastricht" value="1300"/>
        <gemeente id="14" name="Venlo" value="1300"/>
        <gemeente id="19" name="Venray" value="1300"/>
    </provincie>
</results>

当我使用下面的xslt时,结构是o.k.但它显然不会返回id为9,14和19的元素。

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

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

    <xsl:template match="/">
        <results>
            <xsl:apply-templates select="/results/node"/>
         </results>"'
    </xsl:template>

    <xsl:template match="/results/node">
        <xsl:variable name="provname" select="@name"/>
        <xsl:variable name="provid" select="@id"/>
        <provincie id="{$provid}" name="{$provname}">
            <xsl:apply-templates select="./node">
                <xsl:sort select="@value" data-type="number" order="descending"/>
            </xsl:apply-templates>
        </provincie>
    </xsl:template>

    <xsl:template match="results/node/node">
        <xsl:variable name="gemname" select="@name"/>
        <xsl:variable name="gemid" select="@id"/>
        <xsl:variable name="value" select="@value"/>

        <xsl:if test="position() &lt;= 3">
            <gemeente id="{$gemid}" name="{$gemname}" value="{$value}" />
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

不知道现在要遵循哪条路径。下面的代码不起作用。

<xsl:variable name="limitvalue" select="./node[3]/@value"/>
<xsl:if test="@value &gt;= $limitvalue">
    <gemeente id="{$gemid}" name="{$gemname}">
        <xsl:value-of select="@value"/> 
    </gemeente>
</xsl:if>

任何帮助表示赞赏!

根据@ ThomasW的解决方案更新并允许设置limitrank。

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

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

    <!--set limitrank as the rank that we use to cut off-->
    <xsl:variable name="limitrank" select="3"/>

    <xsl:template match="/">
        <results>
            <xsl:apply-templates select="/results/node"/>
        </results>
    </xsl:template>

    <xsl:template match="/results/node">
        <!-- We generate a string of decending @value numbers; 
         Each number covers exactly 5 characters (spaces are added if there are less than 5 digits) -->
        <xsl:variable name="sortedValues">
            <xsl:for-each select="node">
                <xsl:sort select="@value" data-type="number" order="descending"/>
                <!-- Here we add spaces and strip everything that exceeds 5 characters -->
                <xsl:value-of select="substring(concat(@value,'    '),1,5)"/>
            </xsl:for-each>
        </xsl:variable>
        <!-- Now, we get the Nth number, which covers bytes (limitrank-1)*5+1 to limitrank*5;
         So that we get a meaningful result if there were less than three numbers, we add two trailing zeroes -->
        <xsl:variable name="limitValue" select="substring(concat($sortedValues,'0    0'),($limitrank - 1) * 5 + 1,5)"/>

        <provincie>
            <xsl:copy-of select="@id|@name"/>
            <xsl:apply-templates select="node[@value &gt;= $limitValue]">
                <xsl:sort select="@value" data-type="number" order="descending"/>
            </xsl:apply-templates>
        </provincie>
    </xsl:template>

    <xsl:template match="results/node/node">
        <gemeente>
            <xsl:copy-of select="@id|@name|@value"/>
        </gemeente>
    </xsl:template>
</xsl:stylesheet>

1 个答案:

答案 0 :(得分:2)

我不明白这个问题:对于每个省,你按照@value对市政当局进行排序。然后你进入前三名,如果还有其他人与第三名@value相同,你也可以包括那一名。是吗?

我相信这只能用一些字符串技巧来完成(参见代码中的注释):

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

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

  <xsl:template match="/">
    <results>
      <xsl:apply-templates select="/results/node"/>
    </results>
  </xsl:template>

  <xsl:template match="/results/node">
    <!-- We generate a string of decending @value numbers; 
         Each number covers exactly 5 characters (spaces are added if there are less than 5 digits) -->
    <xsl:variable name="sortedValues">
      <xsl:for-each select="node">
        <xsl:sort select="@value" data-type="number" order="descending"/>
        <!-- Here we add spaces and strip everything that exceeds 5 characters -->
        <xsl:value-of select="substring(concat(@value,'    '),1,5)"/>
      </xsl:for-each>
    </xsl:variable>
    <!-- Now, we get the 3rd number, which covers characters 11 to 15;
         So that we get a meaningful result if there were less than three numbers, we add two trailing zeroes -->
    <xsl:variable name="limitValue" select="substring(concat($sortedValues,'0    0'),11,5)"/>

    <provincie>
      <xsl:copy-of select="@id|@name"/>
      <xsl:apply-templates select="node[@value &gt;= $limitValue]"/>
    </provincie>
  </xsl:template>

  <xsl:template match="results/node/node">
    <gemeente>
      <xsl:copy-of select="@id|@name|@value"/>
    </gemeente>
  </xsl:template>
</xsl:stylesheet>

输出如下:

<?xml version="1.0" encoding="UTF-8"?>
<results>
  <provincie id="1" name="GLD">
    <gemeente id="3" name="Arnhem" value="500"/>
    <gemeente id="6" name="Apeldoorn" value="3000"/>
    <gemeente id="8" name="Bennekom" value="750"/>
    <gemeente id="9" name="Velp" value="500"/>
  </provincie>
  <provincie id="11" name="LI">
    <gemeente id="12" name="Maastricht" value="1300"/>
    <gemeente id="13" name="Heerlen" value="5010"/>
    <gemeente id="14" name="Venlo" value="1300"/>
    <gemeente id="18" name="Geleen" value="1750"/>
    <gemeente id="19" name="Venray" value="1300"/>
  </provincie>
</results>

如果您希望对市政当局进行分类,那么当然您必须包含另一个xsl:sort

    <provincie>
      <xsl:copy-of select="@id|@name"/>
      <xsl:apply-templates select="node[@value &gt;= $limitValue]">
        <xsl:sort select="@value" data-type="number" order="descending"/>
      </xsl:apply-templates>
    </provincie>