Reputation: 33
I am facing a problem while creating an XSLT for the below transformation. I am relatively new to XSLT transformations. The problem is that i want to count the number of students from distinct countries in the input. I have tried counting based on conditions but it doesn't work as this counting is grouped. Can someone tell me if this can be done is XSLT 1.0.
My Input is
<ns0:DetailsResponse xmlns:ns0="http://MySchema.XSLTSchema">
<Class>1</Class>
<Students>
<StudentName>John</StudentName>
<StudentSurname>Doe</StudentSurname>
<Country>
<CountryName>UK</CountryName>
</Country>
</Students>
<Students>
<StudentName>Cherry</StudentName>
<StudentSurname>Blossom</StudentSurname>
<Country>
<CountryName>US</CountryName>
</Country>
</Students>
<Students>
<StudentName>Ankit</StudentName>
<StudentSurname>Sood</StudentSurname>
<Country>
<CountryName>INDIA</CountryName>
</Country>
</Students>
<Students>
<StudentName>Peter</StudentName>
<StudentSurname>Scott</StudentSurname>
<Country>
<CountryName>UK</CountryName>
</Country>
</Students>
<Students>
<StudentName>Joe</StudentName>
<StudentSurname>Carter</StudentSurname>
<Country>
<CountryName>UK</CountryName>
</Country>
</Students>
<Students>
<StudentName>Anu</StudentName>
<StudentSurname>Mehta</StudentSurname>
<Country>
<CountryName>INDIA</CountryName>
</Country>
</Students>
</ns0:DetailsResponse>
and I want my Output to be like
Output
<ns0:Root xmlns:ns0="http://MySchema.XSLTSchema_Destination">
<DestinationClass>DestinationClass_0</DestinationClass>
<Countries>
<CountryWiseCount>
<Country>INDIA</Country>
<Count>2</Count>
</CountryWiseCount>
<CountryWiseCount>
<Country>UK</Country>
<Count>3</Count>
</CountryWiseCount>
<CountryWiseCount>
<Country>US</Country>
<Count>1</Count>
</CountryWiseCount>
</Countries>
</ns0:Root>
Upvotes: 3
Views: 3765
Reputation: 70648
If you are using XSLT 1.0, then Muenchian Grouping will be your friend here.
You are grouping students by country name, so you define a key to look up students like so
<xsl:key name="students" match="Students" use="Country/CountryName"/>
Then, to get the distinct countries, you would match the Students elements that happen to be the elements that occur first in the key for their given country.
<xsl:template
match="Students[generate-id() = generate-id(key('students', Country/CountryName)[1])]">
Then to get the count of all the students for that country, you can just count the key:
<xsl:value-of select="count(key('students', Country/CountryName))"/>
Here is the full XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="students" match="Students" use="Country/CountryName"/>
<xsl:template match="Students[generate-id() = generate-id(key('students', Country/CountryName)[1])]">
<CountryWiseCount>
<Country>
<xsl:value-of select="Country/CountryName"/>
</Country>
<Count>
<xsl:value-of select="count(key('students', Country/CountryName))"/>
</Count>
</CountryWiseCount>
</xsl:template>
<xsl:template match="Students"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your XML, the following is output
<ns0:DetailsResponse xmlns:ns0="http://MySchema.XSLTSchema">
<Class>1</Class>
<CountryWiseCount>
<Country>UK</Country>
<Count>3</Count>
</CountryWiseCount>
<CountryWiseCount>
<Country>US</Country>
<Count>1</Count>
</CountryWiseCount>
<CountryWiseCount>
<Country>INDIA</Country>
<Count>2</Count>
</CountryWiseCount>
</ns0:DetailsResponse>
Note the use of the template <xsl:template match="Students"/>
which matches the Students elements which are not first in the key, to stop them being output. The XSLT will always give priority to the more specific template (with the xpath expression), so this template won't ignore everything.
Obviously you would need to extend the XSLT with a template to match class too, but I am sure you can work that out.
Upvotes: 2