tags:

views:

41

answers:

4

My XML is

<row>
  <entry>1.</entry>
  <entry>foo</entry>
  <entry>morefoo</entry>
</row>
<row>
  <entry>2.</entry>
  <entry>2foo</entry>
  <entry>2morefoo</entry>
</row>

using XSLT, i'm trying to represent this information in a html table, and i want to have a Serial no. column.

How do i go about selecting the value of only the first 'entry' tag?

+1  A: 
//row/entry[1]

This will select all the entry tags that are the first child of the parent node.

Niels van der Rest
+1 for being faster than me :)
Dimitre Novatchev
+2  A: 

My XML is

<row> 
<entry>1.</entry> 
<entry>foo</entry> 
<entry>morefoo</entry> 
</row> 
<row> 
<entry>2.</entry> 
<entry>2foo</entry> 
<entry>2morefoo</entry> 
</row>

This is not wellformed XML document. A well-formed XML document must have exactly one top-level element. I will use the following (corrected to be well-formed) XML document:

<rows>
    <row>
        <entry>1.</entry>
        <entry>foo</entry>
        <entry>morefoo</entry>
    </row>
    <row>
        <entry>2.</entry>
        <entry>2foo</entry>
        <entry>2morefoo</entry>
    </row>
</rows>

How do i go about selecting the value of only the first 'entry' tag?

/*/row/entry[1]

The above selects the first entry element-child of every row element.

/*/row[1]/entry[1]

The above selects the first entry element-child of the first row element in the document.

/*/row[2]/entry[1]

The above selects the first entry element-child of the second row element in the document.

(//entry)[1]

The above selects the first entry element in the whole document.

//entry[1]

Note that this is different from the previous expression: this selects every entry element in the document, which is the first entry-child of its parent.

Dimitre Novatchev
+1 for the explanation of different XPath flavors. The [W3C XPath Recommendation](http://www.w3.org/TR/xpath/) contains every detail on XPath expressions, in case @charudatta is interested.
Niels van der Rest
+1 from me too, but a question that might raise: why would `(//entry)[1]` be used instead of just `//entry[1]`?
Abel
@Abel: `(//entry)[1]` isn't equivalent to `//entry[1]` and they both have their uses.
Dimitre Novatchev
I know, I was just trying to make a point, sorry if it added to the confusion ;)
Abel
@Abel: No problem, I added this last expression in my answer, without lengthy explanations.
Dimitre Novatchev
+1  A: 

The XSLT below will produce this table:

Serial No  Name   Description
1.         foo    morefoo 
2.         2foo   2morefoo 

Sample document:

<?xml version="1.0" encoding="utf-8"?>
<document>
  <row>
    <entry>1.</entry>
    <entry>foo</entry>
    <entry>morefoo</entry>
  </row>
  <row>
    <entry>2.</entry>
    <entry>2foo</entry>
    <entry>2morefoo</entry>
  </row>
</document>

XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:output method="html" indent="yes"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>Sample</title>
      </head>
      <body>
        <table border="1">
          <tr>
            <th>Serial No</th>
            <th>Name</th>
            <th>Description</th>
          </tr>
          <xsl:apply-templates />
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="row">
    <tr>
      <xsl:for-each select="entry">
        <td>
          <xsl:value-of select="."/>
        </td>
      </xsl:for-each>
    </tr>
  </xsl:template>

</xsl:stylesheet>
0xA3
+1 for a good working example (and you were quicker than me ;-). I wouldn't opt for the for-each though, which in this case makes it (slightly) harder to distinguish between first cell and the rest.
Abel
+2  A: 

In your XSLT you should have something like this:

<!-- matches the root (not specified in your post) -->
<xsl:template match="/">
   <!-- at root level, make your table -->
   <table>
     <thead>.... </thead>
     <tbody>
       <!-- push the rows through, make sure the path is correct -->
       <xsl:apply-templates select="path/to/row" />
     </tbody>
   </table>
</xsl:template>

<xsl:template match="row">
   <!-- create the rows -->
   <tr>
     <!-- process first entry only, as you requested (why?) -->
     <xsl:apply-templates select="entry[1]" mode="first"/>
     <!-- process other entries -->
     <xsl:apply-templates select="entry[position() > 1]" mode="other"/>
   </tr>
</xsl:template>

<xsl:template match="entry" mode="first">
   <!-- maybe you want the first cell to be treated specially, i.e. as row header -->
   <th><xsl:value-of select="."/></th>
</xsl:template>

<xsl:template match="entry" mode="other">
   <!-- the other cells -->
   <td><xsl:value-of select="."/></td>
</xsl:template>

Note: the code above is not tested, use it as a template for your own real code and data.

Output would be something like the following:

<table>
  <thead>....</thead>
  <tbody>
    <tr>
      <th>1.</th>
      <td>foo</td>
      <td>morefoo</td>
    </tr>
    <tr>
      <th>2.</th>
      <td>2foo</td>
      <td>2morefoo</td>
    </tr>
    <tr>....
  </tbody>
</table>
Abel
+1 for the extensive XSLT code.
Dimitre Novatchev
@Abel: good answer! But, I think it would be even better example with more "pattern matching" as `match="entry[1]"` and without apply-templates/@select... It would reflect better that it is just transforming `Row` to `TR` and `entry` to `TD`.
Alejandro