Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT to sort on value of multiple attributes

Tags:

sorting

xslt

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.

like image 917
Torben Gundtofte-Bruun Avatar asked Nov 25 '11 09:11

Torben Gundtofte-Bruun


1 Answers

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".

like image 92
Vincent Biragnet Avatar answered Oct 24 '22 10:10

Vincent Biragnet