Reputation: 416
I'm trying to sort xml with xslt but the problem is that my code only sort elements under specific node, here is a sample xml:
<region name="Germany">
<company name="Mercedes" rank="2" />
<company name="BMW" rank="3" />
</region>
<region name="Japan">
<company name="Toyota" rank="1" />
<company name="Mazda" rank="4" />
</region>
I tried the folowing but it didn't work
<xsl:template match="region">
<Companies>
<xsl:for-each select="company">
<xsl:sort select="@rank" />
<xsl:call-template name="companies">
</xsl:for-each>
</Companies>
</xsl:template>
<xsl:template name="companies">
<Company>
<xsl:value-of select="@name" />
</Company>
</xsl:template>
the output should be:
<Companies>
<Company>Toyota</Company>
<Company>Mercedes</Company>
<Company>BMW</Company>
<Company>Mazda</Company>
</Companies>
Upvotes: 0
Views: 749
Reputation: 167516
You haven't shown any container element for those region
elements but assuming you have them inside one common container write a template matching that container (e.g. named root
in the sample code below) and then I would simply suggest to apply-templates to the company
grandchildren with an xsl:sort
included based on the rank
attribute.
Then add a template transforming from the attribute based company
input elements to a value based element and you are done:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="root">
<Companies>
<xsl:apply-templates select="region/company">
<xsl:sort select="xs:integer(@rank)"/>
</xsl:apply-templates>
</Companies>
</xsl:template>
<xsl:template match="company">
<xsl:copy>
<xsl:value-of select="@name"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/94rmq6B
Matching on each region
doesn't seem to make sense if you want to process them all together in a sorted way.
Upvotes: 1
Reputation: 8225
From the output it seems you want to sort by rank and not name.
Regarding the sorting happening withing the node, since your template is running for each region and so the sorting is for company nodes within a region. You could run template for parent of region and then iterate over elements and sort by name. Here is the template with the matching output.
<xsl:template match="*[region]">
<Companies>
<xsl:for-each select="region/company">
<xsl:sort select="@rank" />
<xsl:call-template name="companies" />
</xsl:for-each>
</Companies>
</xsl:template>
<xsl:template name="companies">
<Company>
<xsl:value-of select="@name" />
</Company>
</xsl:template>
Upvotes: 1