I have a huge XML-formatted configuration file. The system doesn't care about the order of tags, but we humans do! (Primarily for the purpose of version comparisons.) I already received the XSLT below which works well, but I've discovered that it's not quite enough.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates>
<xsl:sort select="(@name, name())[1]"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I want to sort all tags recursively by the value of their name
attribute (this works!) but because the attribute is not always present, it must also sort by further attributes, any of which may or may not be present in any given element.
I have basically zero understanding of XSLT so I'm experimenting. I've hacked the above into this, but it doesn't work as desired. The result of this seems to be identical to the above.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates>
<xsl:sort select="@name"/>
<xsl:sort select="@row" data-type="number"/>
<xsl:sort select="@col" data-type="number"/>
<xsl:sort select="@sequence" data-type="number"/>
<xsl:sort select="@tabindex" data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
My data looks similar to this, and the problem is that the cell
elements are not sorted at all (within their grid
group) because they have no name
attribute. This is why I'd like to extend the sorting logic to use name
attribute when present, else the sort should be done using additional attributes like tabindex
. Within any given group, the same attributes can be assumed to be present.
<sections>
<section name="SomeList">
<caption>
<![CDATA[Candidates]]>
</caption>
...
<parameters>
<parameter name="pageSize">
<![CDATA[50]]>
</parameter>
</parameters>
...
<grid>
<cell row="0" col="7" tabindex="9" colspan="10">
<field name="Entered" />
</cell>
</grid>
</section>
</sections>
Update:
With Vincent's very good help, I've created a sorting that works well enough for our purposes. Here it is.
Consider this XSLT for soecific elements with given mandatory attributes :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates>
<xsl:sort select="(@name, name())[1]"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="grid">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each-group select="*" group-by="if (exists(@row)) then @row else -1">
<xsl:sort select="current-grouping-key()" data-type="number"/>
<xsl:for-each-group select="current-group()" group-by="if (exists(@col)) then @col else -1">
<xsl:sort select="current-grouping-key()" data-type="number"/>
<xsl:apply-templates select="current-group()"/>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
My example must cover sections and parameters sorting with the first template matching *. And also grid sorting by row and col. You can extend for any orther elements that has different sorting attributes by duplicating the template.
If you've got several elements for the same attributes, use match="elt1|elt2|elt3"
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With