Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selecting the unique values from multiple attributes in an XML document using XSLT 1.0

Tags:

xml

xslt

I have the following XML:

<League>
  <Week Date="26/04/2010 19:00">
    <Fixture Id="542" HomeTeamId="371" HomeTeam="London Raiders Green" AwayTeamId="13" AwayTeam="Richmond Swingers" />
    <Fixture Id="543" HomeTeamId="45" HomeTeam="Spartans" AwayTeamId="15" AwayTeam="Panthers" />
    <Fixture Id="544" HomeTeamId="370" HomeTeam="Fat Cats" AwayTeamId="381" AwayTeam="London Raiders Orange" />
  </Week>
  <Week Date="27/04/2010 19:00">
    <Fixture Id="548" HomeTeamId="3" HomeTeam="The Mob" AwayTeamId="81" AwayTeam="London Raiders Red" />
    <Fixture Id="549" HomeTeamId="373" HomeTeam="Intellect" AwayTeamId="83" AwayTeam="Tornadoes" />
  </Week>
</League>

What I would like to do is get a unique list of all the Team Ids within that XML, but the problem is that team ids can either appear in the HomeTeamId or the AwayTeamId attributes in the fixture nodes. So I'm struggling to use the standard grouping methods (Grouping using the Muenchian method or selecting unique nodes by checking the preceding sibling).

I can get a list of all the ids this way:

<xsl:for-each select="//Fixture/@HomeTeamId | //Fixture/@AwayTeamId">
    <xsl:sort select="."/>
    <xsl:value-of select="."/><br/>
</xsl:for-each>

But of course when the teams appear in more than one fixture, their id is output more than once using the for-each above.

My ultimate aim is to output a list of fixtures grouped by each team, but I'm struggling with the XSLT here - it's making my brain hurt... Can anyone point me in the right direction?

like image 616
MajorRefactoring Avatar asked Dec 11 '25 03:12

MajorRefactoring


2 Answers

It shouldn't be a problem to use Muenchian grouping:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:output indent="yes"/>

<xsl:key name="k1" match="Fixture/@HomeTeamId | Fixture/@AwayTeamId" use="."/>

<xsl:template match="/">
  <html>
    <head>
      <body>
        <xsl:for-each select="(//Fixture/@HomeTeamId | //Fixture/@AwayTeamId)[generate-id() = generate-id(key('k1', .)[1])]">
          <xsl:sort select="." data-type="number"/>
          <xsl:value-of select="."/>
          <br/>
        </xsl:for-each>
      </body>
    </head>
  </html>
</xsl:template>

</xsl:stylesheet>

I wouldn't use a for-each but instead do apply-templates but as you already had a for-each in your sample I kept that to show how to use the Muenchian grouping with that.

like image 148
Martin Honnen Avatar answered Dec 12 '25 18:12

Martin Honnen


This transformation produces the unique team ids even if their values belonged to any number of differently named attributes:

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

 <xsl:key name="kTeamById"
  match="@*[name()=document('')/*/my:teamIdNames/*]" use="."/>

 <my:teamIdNames>
   <name>HomeTeamId</name>
   <name>AwayTeamId</name>
 </my:teamIdNames>

 <xsl:variable name="vAttrNames" select=
  "document('')/*/my:teamIdNames/*"/>

 <xsl:template match="/">
  <xsl:apply-templates select=
   "//Fixture/@*[name()=$vAttrNames]"/>
 </xsl:template>

 <xsl:template match=
 "@*[name()=document('')/*/my:teamIdNames/*]
     [generate-id()
     =
      generate-id(key('kTeamById', .)[1])
     ]
 ">
  <xsl:value-of select="."/><br />
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<League>
    <Week Date="26/04/2010 19:00">
        <Fixture Id="542" HomeTeamId="371"
        HomeTeam="London Raiders Green"
        AwayTeamId="13" AwayTeam="Richmond Swingers"/>
        <Fixture Id="543" HomeTeamId="45"
        HomeTeam="Spartans" AwayTeamId="15"
        AwayTeam="Panthers"/>
        <Fixture Id="544" HomeTeamId="370"
        HomeTeam="Fat Cats" AwayTeamId="381"
        AwayTeam="London Raiders Orange" />
    </Week>
    <Week Date="27/04/2010 19:00">
        <Fixture Id="548" HomeTeamId="3"
        HomeTeam="The Mob" AwayTeamId="81"
        AwayTeam="London Raiders Red"/>
        <Fixture Id="549" HomeTeamId="373"
        HomeTeam="Intellect" AwayTeamId="83"
        AwayTeam="Tornadoes"/>
    </Week>
</League>

the wanted result is produced:

371<br/>13<br/>45<br/>15<br/>370<br/>381<br/>3<br/>81<br/>373<br/>83<br/>

Do note:

This solution doesn't assume that the sources of the ids for the teams come just from the attributes named HomeTeamId and AwayTeamId.

In fact, any variable set of names can be used (and included in a separate document), so this is a very powerful, generic solution.

like image 21
Dimitre Novatchev Avatar answered Dec 12 '25 18:12

Dimitre Novatchev



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!