tags:

views:

205

answers:

1

I have a situation where loop through a sorted nodeset and apply a template on each of the nodes:

<div id="contractscontainer">
  <xsl:for-each select="document">
    <xsl:sort select="content[@name='ClientName']/text()" />
    <xsl:apply-templates select="." mode="client-contract" />
  </xsl:for-each>
</div>

I want to do something special with the "first" 5 nodes in the node set and render them nested element. The problem is that they need to be in the same order as if they were sorted (as they are in the loop).

I had planned on doing this by using two xsl:for-each elements, each with the correct nodes selected from the set. I can't do this, however, because they need to be sorted before I can select the "first" 5.

Example:

<div id="contractscontainer">
  <div class="first-five">
    <xsl:for-each select="document[position() < 6]">
      <xsl:sort select="content[@name='ClientName']/text()" />
      <xsl:apply-templates select="." mode="client-contract" />
    </xsl:for-each>
  </div>
  <div class="rest-of-them">
    <xsl:for-each select="document[position() > 5]">
      <xsl:sort select="content[@name='ClientName']/text()" />
      <xsl:apply-templates select="." mode="client-contract" />
    </xsl:for-each>
  </div>
</div>

I don't think this will work because I'm selecting the nodes by position before sorting them, but I can't use xsl:sort outside of the xsl:for-each.

Am I approaching this incorrectly?

Edit: My current solution is to sort them and store the sorted set in another variable:

<xsl:variable name="sorted-docs">
  <xsl:for-each select="document">
    <xsl:sort select="content[@name='ClientName']/text()" />
    <xsl:copy-of select="." />
  </xsl:for-each>
</xsl:variable>

It works, but is there a better way?

+4  A: 

Here is one way to do this in XSLT 1.0 without the need to use the xxx:node-set() extension function:

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

 <xsl:template match="/*">
  <html>
   <div class="first-five">
     <xsl:apply-templates select="num">
       <xsl:sort select="." data-type="number"/>
       <xsl:with-param name="pStart" select="1"/>
       <xsl:with-param name="pEnd" select="5"/>
     </xsl:apply-templates>
   </div>
   <div class="rest-of-them">
     <xsl:apply-templates select="num">
       <xsl:sort select="." data-type="number"/>
       <xsl:with-param name="pStart" select="6"/>
     </xsl:apply-templates>
   </div>
  </html>
 </xsl:template>

 <xsl:template match="num">
  <xsl:param name="pStart"/>
  <xsl:param name="pEnd" select="999999999999"/>

  <xsl:if test="position() >= $pStart
              and
                not(position() > $pEnd)">
   <p><xsl:value-of select="."/></p>
  </xsl:if>
 </xsl:template>

</xsl:stylesheet>

When the above transformation is applied on this simple XML document:

<nums>
  <num>5</num>
  <num>3</num>
  <num>6</num>
  <num>8</num>
  <num>4</num>
  <num>1</num>
  <num>9</num>
  <num>2</num>
  <num>7</num>
  <num>10</num>
</nums>

the wanted result is produced:

<html>
    <div class="first-five">
        <p>1</p>
        <p>2</p>
        <p>3</p>
        <p>4</p>
        <p>5</p>
    </div>
    <div class="rest-of-them">
        <p>6</p>
        <p>7</p>
        <p>8</p>
        <p>9</p>
        <p>10</p>
    </div>
</html>
Dimitre Novatchev
Solved my problem, thanks
Elijah Glover