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 sections. 1. Horses node has the performance details of individual horses as well as an id to who their sire was. 2. Sires node is list of the Sires also holding breeding specific statistics.
I need to sort the list of sires based on the sum of the 'stake' money won by their foals (i.e. in the horses node).
So a cut down xml file looks like this:
<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>
So summing the various stakes the output would have this ordering:
Sire 101 (srB) Stakes: $25415
Sire 100 (srA) Stakes: $20000
Sire 103 (srC) Stakes: $16768.
When I was just summing and ordering the individual horse stakes for other web pages I was able to use:
<xsl:apply-templates select="Horse">
<xsl:sort select="sum(descendant::Stakes)" data-type="number"
order="descending"/>
</xsl:apply-templates>
I just can't figure out how to do the same for the sires referencing the horse node for summing to get the right ordering... possibly something like this where I try to say the Sire/ID is equal to the Horse/SireID:
<xsl:apply-templates select="Sire">
<xsl:sort select="sum(//Horses/Horse[@SireID=ID]/descendant::Stakes)"
data-type="number" order="descending"/>
</xsl:apply-templates>
But that doesn't work, the debugger jumps straight out of the current template when it hits the sort line so my syntax must be invalid. I've been trying variations on this theme without success.
Can anyone please give me a pointer of how to call my Sire template and get the correct ordering?
Thanks,
Bryce Stenberg
You need to use
current()
:current()
gives you the context node as it is outside of the current XPath expression; in this case, the current<Sire>
element selected byxsl:apply-templates
. Inside the predicate, the context node is the<Horse>
element that's being tested against the predicate expression.Notes:
@
in there that you don't want;@ID
refers to an ID attribute.descendant::/
with//
//foo[barID = current()/ID]
.This transformation:
when applied on the provided XML document:
produces the wanted, correct result:
Explanation:
We define a key that specifies a
Horse
as a function of itsSireID
. This is useful in selecting all offspring of a givenSire
just by providing itsID
in a call to the standard XSLTkey()
function -- like this:key('kOffspring', ID)
.Similarly, the sum of
Stakes
of all offspring of a givenSire
is:sum(key('kOffspring', ID)/*/Stakes)
.We apply templates to all
Sire
elements in the XML document and sort these by the decreasing values of the sums of theStakes
of their offsprings.For each
Sire
we output itsID
,Name
and the sum ofStakes
of its offspring.