tags:

views:

227

answers:

3

I have an XML file which I'm trying to transform into XHTML using a XSLT file. I was wondering if it's possible to get a count of the number of times a template has been invoked. This is my XML:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" encoding="UTF-8" href="car.xslt" version="1.0"?>
<vehicle>
  <car>
    <make>Honda</make>
    <color>blue</color>
  </car>
  <car>
    <make>Saab</make>
    <color>red</color>
  </car>
</vehicle>

And this is my XSLT:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns="http://www.w3.org/1999/xhtml" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<body>
  <table cellpadding="5" border="1">
    <tr><td>number</td><td>make</td><td>color</td></tr>
        <xsl:apply-templates/>
  </table>
</body>
</html>
</xsl:template>

<xsl:template match="car">
    <tr>
      <td>#</td><td><xsl:value-of select="make"/></td><td><xsl:value-of select="color"/></td>
    </tr>
</xsl:template>

</xsl:stylesheet>

I want to print the number of times a car has been printed in the place of #, so it would look like this:

number make color 1 Honda blue 2 Saab red

instead of:

number make color # Honda blue # Saab red

Anybody have any ideas? I was hoping this could be done with just xsl:value-of and XPath

+2  A: 

Replace

#

With

<xsl:value-of select="position()"/>
ChaosPandion
that prints:number make color 2 Honda blue 4 Saab redbut I wantednumber make color 1 Honda blue 2 Saab redI'm checking to see if there's anyway to give arguments to position about depth, etc....
Corey
you could say, position()*0.5... but why is everything being multiplied by two?
Corey
When you use `position()` in this context, XSLT is enumerating the child nodes of the `vehicle` element. The child nodes in the original example are: (1) a text node (a bunch of whitespace); (2) a `car` element; (3) a text node (more whitespace); (4) a `car` element; (5) a text node (more whitespace). So, when you match on `car` elements, you get element number 2 and number 4.
Greg Hewgill
+1  A: 

You can rearrange this a bit so that instead of using <xsl:apply-templates/>, do something like this:

<tr><td>number</td><td>make</td><td>color</td></tr>
<xsl:for-each select="/vehicle/car">
  <tr>
    <td><xsl:value-of select="position()" /></td><td>...</td>
  </tr>
</xsl:for-each>

In this case, the position() function will refer to the iteration number of the associated for-each loop. This may give you what you're looking for.

Greg Hewgill
This would be my preferred method. I was just too lazy to type it out.
ChaosPandion
+1  A: 

Minimal change:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns="http://www.w3.org/1999/xhtml"
>
  <xsl:template match="/vehicle">
    <html>
      <body>
        <table cellpadding="5" border="1">
          <tr>
            <td>number</td>
            <td>make</td>
            <td>color</td>
          </tr>
          <xsl:apply-templates select="car" />
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="car">
    <tr>
      <td><xsl:value-of select="position()" /></td>
      <td><xsl:value-of select="make" /></td>
      <td><xsl:value-of select="color" /></td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

Note the select="car" attribute on the <xsl:apply-templates>. It makes sure that only <car> nodes are counted, so your position() is not off.

Also note that the main template now matches the document element, not the root node.

Tomalak