views:

49

answers:

2

Good afternoon, gentlemen. Help me solve a very simple task.

I have a set of nodes

<menuList>
  <mode name="aasdf"/>
  <mode name="vfssdd"/>
  <mode name="aswer"/>
  <mode name="ddffe"/>
  <mode name="ffrthjhj"/>
  <mode name="dfdf"/>
  <mode name="vbdg"/>
  <mode name="wewer"/>
  <mode name="mkiiu"/>
  <mode name="yhtyh"/>
  and so on...
</menuList>

I have it sorted now this way

 <xsl:variable name="rtf">
    <xsl:for-each select="//menuList/mode">
       <xsl:sort data-type="text" order="ascending" select="@name"/>
          <xsl:value-of select="@name"/>
    </xsl:for-each>
 </xsl:variable>

Now I need to get an arbitrary element in the sorted array to the number of its position. I write code

<xsl:value-of select="exsl:node-set($rtf)[position() = 3]"/>

and get a response error. How to do it right?

+2  A: 

You use <xsl:value-of> in your variable. This does not copy the node, but its string value (the node's @name attribute value). This means you produce a variable containing a concatenated string, but nothing with a structure.

Try:

<xsl:variable name="rtf">
  <xsl:for-each select="//menuList/mode">
    <xsl:sort select="@name" data-type="text" order="ascending" />
    <xsl:copy-of select="."/>
  </xsl:for-each>
</xsl:variable>

Now your variable contains a result tree fragment consisting of <mode> nodes in your preferred order, which means that this:

<xsl:value-of select="exsl:node-set($rtf)/*[3]/@name" />
<!-- don't forget you have element nodes now! ^^^^^^ -->

would work. Also note that …/*[position() = 3] and …/*[3] are the same thing.

Tomalak
@TomalakI did everything as you said. But in response to received nothing.
Kalinin
@Tomalak, Actually, your solution isn't correct and isn't producing anything.You are comitting mistake 2. described in my answer.
Dimitre Novatchev
Also you claim that the `<xsl:value-of select="@name"/>` in the OP's code doesn't produce anything. In fact, this produces a text node, consisting of the string value of the `@name` attribute.
Dimitre Novatchev
@Dimitre, you are right, that was sloppiness on my part. I've corrected my answer in this regard.
Tomalak
@Tomalak, yes it works.
Kalinin
@Tomalak Glad I spotted it. Now it's OK.
Dimitre Novatchev
+2  A: 

There are at least two errors in the provided code:

  1. <xsl:value-of select="@name"/>

When more than one adjacent text node exist, they are combined into one. The result is that the RTF has just one (long) single text node, and there isn't a 3rd node.

2.<xsl:value-of select="exsl:node-set($rtf)[position() = 3]"/>

This asks for the third node contained in exsl:node-set($rtf), however exsl:node-set($rtf) is the document node of the temporary tree produced by the exsl:node-set() extension function -- this is only one node. Therefore the above XPath expression doesn't select anything at all.

One correct solution is the following:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 >

 <xsl:template match="/">
    <xsl:variable name="rtf">
        <xsl:for-each select="//menuList/mode">
           <xsl:sort data-type="text" order="ascending" select="@name"/>
              <xsl:copy-of select="."/>
        </xsl:for-each>
     </xsl:variable>
    <xsl:value-of select="msxsl:node-set($rtf)/*[position()=3]/@name"/>
 </xsl:template>
</xsl:stylesheet>
Dimitre Novatchev
Thank you very much. I have attached a your code and everything turned out. Without you I would not have coped. I have this sort was necessary for my last question: "XSL: List divided into columns.". Now (with your help) the code works completely correctly (first is sorting, then the partition of the columns). Thank you again.
Kalinin