hybrid9
hybrid9

Reputation: 2636

XSL sort numbers from different nodes

I need to construct a string from numbers located in different nodes in my xml source, but the caveats are that the datarate nodes order will be random and the starting number has to be start with at least 600. If there is no 600, then start with 800.

<datarate rate="200" />
<datarate rate="600" />
<datarate rate="300" />
<datarate rate="400" />
<datarate rate="800" />
<datarate rate="1000" />

http://example.com/src/600/800/

I've tried sorting based on groups, but to no avail. Anyone out there have an idea?

Thanks for any help you can provide!

Upvotes: 1

Views: 185

Answers (3)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243449

This short and simple transformation (no explicit conditional instruction used at all -- no xsl:choose, no xsl:when, no xsl:otherwise):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/*">
  <t>
    <xsl:apply-templates select="*">
     <xsl:sort select="@rate" data-type="number"/>
    </xsl:apply-templates>
  </t>
 </xsl:template>

 <xsl:template match=
  "datarate
     [not(@rate >= 600)
     or
      not(../*[@rate = 600]) and not(@rate >= 800)
     ]"/>
</xsl:stylesheet>

when applied on the provided XML document (wrapped into a single top element to be made well-formed):

<t>
    <datarate rate="200" />
    <datarate rate="600" />
    <datarate rate="300" />
    <datarate rate="400" />
    <datarate rate="800" />
    <datarate rate="1000" />
</t>

produces the wanted, correct result:

<t>
   <datarate rate="600"/>
   <datarate rate="800"/>
   <datarate rate="1000"/>
</t>

when applied on the following, slightly changed XML document (no rate of 600, but rates of 601 and 650):

<t>
    <datarate rate="200" />
    <datarate rate="601" />
    <datarate rate="650" />
    <datarate rate="300" />
    <datarate rate="400" />
    <datarate rate="800" />
    <datarate rate="1000" />
</t>

again the correct result is produced:

<t>
   <datarate rate="800"/>
   <datarate rate="1000"/>
</t>

Upvotes: 0

Dabbler
Dabbler

Reputation: 9863

With XSLT 2 you can do this:

<xsl:template match="/">
   <xsl:for-each select="//datarate[number(@rate) ge 600]">
      <xsl:sort select="@rate" data-type="number"/>
      <xsl:value-of select="@rate"/>
      <xsl:if test="not(position() eq last())">
         <xsl:text>, </xsl:text>
      </xsl:if>
   </xsl:for-each>
</xsl:template>

Upvotes: 1

Pavel Veller
Pavel Veller

Reputation: 6115

If you knew the threshold number upfront you could do a very simple transform like this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" encoding="UTF-8"/>

    <xsl:template match="/">
        <xsl:apply-templates select="//datarate">
            <xsl:sort select="@rate" data-type="number"/>
        </xsl:apply-templates>
    </xsl:template> 

    <xsl:template match="datarate[@rate &lt; 600]"/>

    <xsl:template match="datarate">
        [<xsl:value-of select="@rate"/>]
    </xsl:template>
</xsl:stylesheet>

Since you need to check if there's a 600 and use 800 as your threshold if no 600 found, you could it like this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="text" encoding="UTF-8"/>

   <xsl:variable name="threshold">
       <xsl:choose>
           <xsl:when test="//datarate[@rate = 600]">600</xsl:when>
           <xsl:otherwise>800</xsl:otherwise>
       </xsl:choose>
   </xsl:variable>

   <xsl:template match="/">
       <xsl:apply-templates select="//datarate">
           <xsl:sort select="@rate" data-type="number"/>
       </xsl:apply-templates>
   </xsl:template>  

   <xsl:template match="datarate[@rate &lt; $threshold]"/>

   <xsl:template match="datarate">
       [<xsl:value-of select="@rate"/>]
   </xsl:template>
</xsl:stylesheet>

In the event there might be neither a 600 nor a 800 you might want to add some more logic to the $threshold variable declaration.

p.s. Having no details about how you would like your string constructed I just printed the @rate values enclosed in [].

Upvotes: 0

Related Questions