Reputation: 91
I have this XML
<root xmlns:temp ="temp.com">
<testNode name="a">
<sample/>
</testNode>
<testNode name="b">
<sample/>
</testNode>
<testNode name="c">
<sample/>
</testNode>
<testNode name="d">
<sample/>
</testNode>
<testNode name="b">
<sample/>
</testNode>
</root>
I would like to write a transform that copies over everything while sorting the testNodes by the value of the name attribute.
The expected output is:
<root xmlns:temp ="temp.com">
<testNode name="a">
<sample/>
</testNode>
<testNode name="b">
<sample/>
</testNode>
<testNode name="b">
<sample/>
</testNode>
<testNode name="c">
<sample/>
</testNode>
<testNode name="d">
<sample/>
</testNode>
</root>
It is possible the namespace is throwing me off, but I cant seem to get the results to sort.
The XSLT I have tried so far is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:temp="temp.com"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()">
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template name="temp:root">
<xsl:copy>
<xsl:apply-templates select="temp:testNode">
<xsl:sort select="@name"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Views: 279
Reputation: 24430
Given the revised info in your question, there are a couple of issues with your current XSLT and source XML:
1) <xsl:template name="temp:root">
should be <xsl:template match="temp:root">
. i.e. you need to use match
to target an element to be transformed, rather than name
which allows you to call a template.
2) Your source XML declares the temp
prefix, but doesn't use it. You should use:
<root xmlns="temp.com">
<testNode name="a">
<sample/>
...to create a default namespace (it doesn't matter that the prefix is different to your XSLT's prefix; they're just aliases for the real namespace). This then means any elements which don't have a namespace assume the temp.com
namespace.
Or
<temp:root xmlns:temp="temp.com">
<temp:testNode name="a">
<temp:sample/>
Whereby you prefix the elements which are defined in your temp.com
namespace with the temp
prefix.
Here's an XSLT Fiddle with both fixed: http://xsltfiddle.liberty-development.net/3NzcBto
NB: If you want your XSLT to be namespace agnostic you can also use the local-name()
function.
<xsl:template match="*[local-name()='root']">
<xsl:copy>
<xsl:apply-templates select="*[local-name()='testNode']">
<xsl:sort select="@name"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
Generally that's not a good idea, since there's a performance overhead to calling the function, and you lose the benefit of namespaces; but it can be helpful in various situations; particularly whilst developing if you're unsure whether an issue's related to a namespace related problem.
Use the xsl:sort
element documented here: https://www.w3schools.com/xml/xsl_sort.asp
Example: XSLT Fiddle
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="utf-8" /> <!-- keeping utf 8 rather than 16 as this will be big -->
<xsl:strip-space elements="*"/>
<!-- By default, copy everything as is -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!-- but sort the child elements of our root element by their name attribute -->
<xsl:template match="/root">
<xsl:copy>
<xsl:apply-templates select="@* | node()">
<xsl:sort select="./@name" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1