tags:

views:

185

answers:

5

hi,

i've an xml file like

<node>
 <elm val="data1"/>
 <elm val="data2"/>
 <elm val="data3"/>
 <elm val="data4"/>
 <elm val="data5"/>
 <elm val="data6"/>
 <elm val="data7"/>
</node>

i need to write an xslt for this xml file to display in a table fomat like

  1 dat1
  2 dat2
  3 dat3
  4 dat4
  5 dat5
  6 dat6
  7 dat7

please help me to do this

+3  A: 
<xsl:tempate match="node">
  <table>
    <xsl:call-template select="elm"/>
  </table>
</xsl:template>

<xsl:template match="elm">
  <tr>
    <td>
      <xsl:value-of select="count(preceding-sibling::elm) + 1"/>
    </td>
    <td>
      <xsl:value-of select="@val"/>
    </td>
  </tr>
</xsl:template>

Alternatively if the exact ouput is required use the following template for elm.

<xsl:template match="elm">
  <xsl:param name="pos" select="count(preceding-sibling::elm) + 1"/>
  <tr>
    <td>
      <xsl:value-of select="$pos"/>
    </td>
    <td>
      <xsl:text>dat</xsl:text>
      <xsl:value-of select="$pos"/>
    </td>
  </tr>
</xsl:template>

Or in case of text output:

<xsl:tempate match="node">
  <xsl:foreach select="elm">
     <xsl:value-of select="count(preceding-sibling::elm) + 1"/>
     <xsl:text> dat</xsl:text>
     <xsl:value-of select="count(preceding-sibling::elm) + 1"/>
     <xsl:text  disable-output-escaping="yes">&amp;#xA;</xsl:text>
  </xsl:foreach>
</xsl:template>
Obalix
A: 
<xsl:template match="node">
   <table>
      <xsl:apply-templates select="elm"/>
   </table>
</xsl:template>

<xsl:template match="elm">
   <tr>
      <td><xsl:value-of select="position()"/></td>
      <td>dat<xsl:value-of select="position()"/></td>
   </tr>
</xsl:template>
buggy1985
See here (http://www.dpawson.co.uk/xsl/sect2/N6099.html) for a reason why position is not ideal.
Obalix
+1  A: 

You can use the position() function

<xsl:template match="elm">
   <xsl:value-of select="concat(position(), ' ', @val)" />
</xsl:template>

Note: The table format in your example does not match the value of the val attribute, but I have assumed that you want both to be the same (output of first line should be 1 data1 instead of 1 dat1.

As @Dimitre Novatchev noted in his answer, this assumes that your original document has no white-space-only nodes. In his answer he deals with this by removing all white-space-only nodes from the input document (using the <xsl:strip-space elements="*"/> directive) and appending a new line character (&#xA;) at the end of the concatenation.

Oded
See here (dpawson.co.uk/xsl/sect2/N6099.html) for a reason why position is not ideal.
Obalix
@Obalix - in this case, `position()` is fine, as the context node is `elm`.
Oded
Why the downvote? `position()` may be misunderstood by some, but is a perfectly fine function to use in certain circumstances, including this one.
Oded
See my answer to understand the flaw in your proposed solution. Never post a solution without having actually performed the transformation -- this will save both your time and that of the readers. More importantly, they will not be misled to use an incorrect solution.
Dimitre Novatchev
@Dimitre Novatchev - thanks for the feedback and information. Answer updated to include white-space-only nodes information.
Oded
A: 

XSLT "variables" are unvarying variables. Once you set them, you can't arbitrarily change them.

The only hacks around it is to use the position() function and other "+1" functions, but you don't get the flexibility of a true variable.

There are many articles about the unvariability of XSLT "variables".

Quoting article on "http://www.xml.com/pub/a/2001/02/07/trxml9.html"

XSLT variables actually have a lot more in common with constants in many programming languages and are used for a similar purpose.

Counters in XSLT are not possible because XSLT is not a procedural language, it's declarative. To the downvoter: please provide reason. If this is wrong, then please prove otherwise.

Wadih M.
+3  A: 

This is wrong:

<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="elm">
     <xsl:value-of select="concat(position(), ' ', @val)"/>
 </xsl:template>
</xsl:stylesheet>

When run with many XSLT processors this will produce the following (unwanted) output:

 2 data1
 4 data2
 6 data3
 8 data4
 10 data5
 12 data6
 14 data7

The reason is that when templates are applied to the children of the top element, this includes the children that are white-space-only text nodes -- between every two consecutive elm elements.

So, Oded's solution is wrong.

Here is one correct solution (and one of the shortest possible):

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>

 <xsl:template match="elm">
     <xsl:value-of select="concat(position(), ' ', @val, '&#xA;')"/>
 </xsl:template>
</xsl:stylesheet>

This transformation produces the correct result:

1 data1
2 data2
3 data3
4 data4
5 data5
6 data6
7 data7

Do note:

  1. The use of the <xsl:strip-space elements="*"/> to direct the XSLT processor to discard any white-space-only text nodes.

  2. The use of the XPath concat() function to glue together the position, the data and the NL character.

Dimitre Novatchev
+1 for being precise as ever!
Obalix
@Obalix: Thanks, your solution is also good. I upvoted it 3 hrs. ago.
Dimitre Novatchev