XSLT: Sorting based on sum of values from other nodes

后端 未结 2 653
抹茶落季
抹茶落季 2021-01-20 03:27

I\'m rather new to coding xslt and have got rather stuck trying to do the following.

I have an xml file that has breeding info for horses broken into two main sectio

相关标签:
2条回答
  • 2021-01-20 03:48

    This transformation:

    <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="kOffspring" match="Horse" use="SireID"/>
    
     <xsl:template match="/*">
      <xsl:apply-templates select="Sires/Sire">
       <xsl:sort select="sum(key('kOffspring', ID)/*/Stakes)"
                 data-type="number" order="descending"/>
      </xsl:apply-templates>
     </xsl:template>
    
     <xsl:template match="Sire">
         Sire <xsl:value-of select="concat(ID,' (', Name, ') Stakes: ')"/>
       <xsl:value-of select="sum(key('kOffspring', ID)/*/Stakes)"/>
     </xsl:template>
     <xsl:template match="text()"/>
    </xsl:stylesheet>
    

    when applied on the provided XML document:

    <t>
        <Horses>
            <Horse>
                <ID>1</ID>
                <Name>hrsA</Name>
                <SireID>101</SireID>
                <Pace>
                    <Stakes>4800</Stakes>
                </Pace>
            </Horse>
            <Horse>
                <ID>2</ID>
                <Name>hrsB</Name>
                <SireID>102</SireID>
                <Pace>
                    <Stakes>3600</Stakes>
                </Pace>
            </Horse>
            <Horse>
                <ID>3</ID>
                <Name>hrsC</Name>
                <SireID>102</SireID>
                <Pace>
                    <Stakes>2800</Stakes>
                </Pace>
            </Horse>
            <Horse>
                <ID>4</ID>
                <Name>hrsD</Name>
                <SireID>101</SireID>
                <Pace>
                    <Stakes>56</Stakes>
                </Pace>
            </Horse>
            <Horse>
                <ID>5</ID>
                <Name>hrsE</Name>
                <SireID>100</SireID>
                <Pace>
                    <Stakes>20000</Stakes>
                </Pace>
            </Horse>
            <Horse>
                <ID>6</ID>
                <Name>hrsF</Name>
                <SireID>101</SireID>
                <Trot>
                    <Stakes>20000</Stakes>
                </Trot>
            </Horse>
            <Horse>
                <ID>7</ID>
                <Name>hrsG</Name>
                <SireID>101</SireID>
                <Trot>
                    <Stakes>559</Stakes>
                </Trot>
            </Horse>
            <Horse>
                <ID>8</ID>
                <Name>hrsH</Name>
                <SireID>102</SireID>
                <Pace>
                    <Stakes>386</Stakes>
                </Pace>
                <Trot>
                    <Stakes>10000</Stakes>
                </Trot>
            </Horse>
        </Horses>
        <Sires>
            <Sire>
                <ID>100</ID>
                <Name>srA</Name>
                <LiveFoalsALL>117</LiveFoalsALL>
            </Sire>
            <Sire>
                <ID>101</ID>
                <Name>srB</Name>
                <LiveFoalsALL>774</LiveFoalsALL>
            </Sire>
            <Sire>
                <ID>102</ID>
                <Name>srC</Name>
                <LiveFoalsALL>43</LiveFoalsALL>
            </Sire>
        </Sires>
    </t>
    

    produces the wanted, correct result:

     Sire 101 (srB) Stakes: 25415
     Sire 100 (srA) Stakes: 20000
     Sire 102 (srC) Stakes: 16786
    

    Explanation:

    1. We define a key that specifies a Horse as a function of its SireID. This is useful in selecting all offspring of a given Sire just by providing its ID in a call to the standard XSLT key() function -- like this: key('kOffspring', ID).

    2. Similarly, the sum of Stakes of all offspring of a given Sire is: sum(key('kOffspring', ID)/*/Stakes) .

    3. We apply templates to all Sire elements in the XML document and sort these by the decreasing values of the sums of the Stakes of their offsprings.

    4. For each Sire we output its ID, Name and the sum of Stakes of its offspring.

    0 讨论(0)
  • 2021-01-20 03:53

    You need to use current():

    <xsl:apply-templates select="Sire">
        <xsl:sort select="sum(//Horses/Horse[SireID = current()/ID]//Stakes)" 
            data-type="number" order="descending"/>
    

    current() gives you the context node as it is outside of the current XPath expression; in this case, the current <Sire> element selected by xsl:apply-templates. Inside the predicate, the context node is the <Horse> element that's being tested against the predicate expression.

    Notes:

    • You had a @ in there that you don't want; @ID refers to an ID attribute.
    • I replaced descendant::/ with //
    • As @Dimitre noted, your stylesheet will scale better to large input if you use keys instead of //foo[barID = current()/ID].
    0 讨论(0)
提交回复
热议问题