Re: [xsl] Newbie question on XSL transformations: multiple sorts on element attributes

Subject: Re: [xsl] Newbie question on XSL transformations: multiple sorts on element attributes
From: Abel Braaksma <abel.online@xxxxxxxxx>
Date: Tue, 06 Feb 2007 22:35:53 +0100
[sorry, I send this earlier, but it got bounced, trying again]

Rob,

I am sorry, I misunderstood you, I see now that I did not read your request well. I corrected your snippet, but that was actually an example of what you did not want to do.

See my comments on your code. Below is a little rewrite of your code (most is left intact) that shows you how to use the correct order.

Cheers,
-- Abel


Rob Newman wrote:
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />

No need to specify xml, 1.0 and utf-8, these are all the defined defaults. This suffices (but is a matter of taste, really):


<xsl:output indent="yes" />


<xsl:template match="sources"> <xsl:element name="dataloggerlist"> <xsl:for-each select="source"> <xsl:apply-templates select="/pfarr/pfarr/pfarr" /> </xsl:for-each> </xsl:element> </xsl:template>

"sources" never matches anything. This template does nothing (or your input contains more). Also, it is generally a better idea to do a select that is relative, unless you really need the absolute path. With your source, it becomes this, as entry point of your template:


  <xsl:template match="/">
      <xsl:apply-templates select="pfarr/pfarr/pfarr" />
  </xsl:template>



<xsl:template match="pfarr/pfarr/pfarr">
<xsl:element name="datalogger">
<xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
<xsl:apply-templates select="pfstring"/>
</xsl:element>
</xsl:template>


The triple path is not needed, because you already select this same path. What you are saying to the XSLT processor here is: any "pfarr" (most right in the match) that has a parent "pfarr" (middle) that has a parent "pfarr". Since you already do a select="pfarr/pfarr/pfarr", which only selects the 'pfarr' children of the 'pfarr' children of 'pfarr', there is no need to let the processor do this extra parent check.

In addition, you can create your element also as a literal result element. But this is matter of taste. Here's what I thought of (note that this in no way changes the result tree, and your code was not in error):

  <xsl:template match="pfarr">
      <datalogger name="{@name}">
          <xsl:apply-templates select="pfstring" />
      </datalogger>
  </xsl:template>



<xsl:template match="pfstring">
<xsl:element name="param">
<xsl:attribute name="id"><xsl:value-of select="@name"/></xsl:attribute>
<xsl:value-of select="." />
</xsl:element>
</xsl:template>

same here, you can make it shorter in the following way (I usually only use xsl:element when I want the name of the element to be created dynamically, same for an attribute):


<xsl:template match="pfstring">
  <param id="{@name}" >
      <xsl:value-of select="." />
  </param>
</xsl:template>



<xsl:sort select="param/@id{dlt}" data-type="number" order="descending" />
<xsl:sort select="@name" order="ascending" />

I see now that you want to:


1. put all elements with id = 'dlt' first
2. order these first elements by number value
3. next, order all other elements, not containing 'dlt' by there id


This looks like this, in XSLT (but is not trivial)


<xsl:sort select="@name = 'dlt'" order="descending"/>
<xsl:sort select="self::node()[@name = 'dlt']" data-type="number" order="ascending"/>
<xsl:sort select="@name" />


Finally, this is the complete stylesheet that does what you want:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
<xsl:output indent="yes" />
<xsl:template match="/">
<xsl:apply-templates select="pfarr/pfarr/pfarr" />


</xsl:template>
<xsl:template match="pfarr">
<datalogger name="{@name}">
<xsl:apply-templates select="pfstring">
<xsl:sort select="@name = 'dlt'" order="descending"/>
<xsl:sort select="self::node()[@name = 'dlt']" data-type="number" order="ascending"/>
<xsl:sort select="@name" />
</xsl:apply-templates>
</datalogger>
</xsl:template>


  <xsl:template match="pfstring">
      <param id="{@name}" >
          <xsl:value-of select="." />
      </param>
  </xsl:template>
</xsl:stylesheet>

Current Thread