Dan
Dan

Reputation: 5986

XSLT get nth node using dynamic number

I need to add CSS classes to each item output by an XSLT template. The class values need to include the position of the node, which is fine, but the position needs to be written in words (classOne, classTwo etc) rather than a digits (class1, class2 etc).

The code I have almost works. It outputs the position correctly as a number, but when I use that position to return the written version of the number it just picks the first one every time, so I always get a class of 'classOne'. If I hard-code the number it works fine.

<xsl:param name="currentPage"/>
<xsl:variable name="numbers" select="my.library:Split('One,Two,Three,Four,Five,Six,Seven,Eight',',')"/>

<xsl:template match="/">
    <xsl:apply-templates select="$currentPage/*[starts-with(name(), 'largeImage')]" mode="large" />
</xsl:template>

<xsl:template match="*" mode="large">
  <xsl:variable name="index" select="substring(name(), 11)"/>
  <div class="class{$numbers/*[$index]}">item</div>
</xsl:template>

Can anyone see how I can get it to convert the $index value into the written equivalent?

Upvotes: 1

Views: 1826

Answers (3)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243529

No string-splitting and extension functions are necessary to carry out the required processing:

I. XSLT 1.0 Solution

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my" exclude-result-prefixes="my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <my:nums>
  <num>one</num>
  <num>two</num>
  <num>three</num>
  <num>four</num>
  <num>five</num>
  <num>six</num>
  <num>seven</num>
  <num>eight</num>
  <num>nine</num>
  <num>ten</num>
 </my:nums>

 <xsl:variable name="vNums" select="document('')/*/my:nums/*"/>

 <xsl:template match="*[starts-with(name(), 'largeImage')]">
   <xsl:variable name="vIndex">
     <xsl:number count="*[starts-with(name(), 'largeImage')]"
                 level="any"/>
   </xsl:variable>
   <div class="class{$vNums[position() = $vIndex]}">item</div>
 </xsl:template>

</xsl:stylesheet>

when applied on this XML document (no source XML document was provided!):

<t>
 <largeImageA/>
 <largeImageB/>
 <largeImageC/>
 <largeImageD/>
 <largeImageE/>
 <largeImageF/>
 <largeImageG/>
 <largeImageH/>
 <largeImageI/>
 <largeImageJ/>
</t>

produces the wanted, correct result:

<div class="classone">item</div>
<div class="classtwo">item</div>
<div class="classthree">item</div>
<div class="classfour">item</div>
<div class="classfive">item</div>
<div class="classsix">item</div>
<div class="classseven">item</div>
<div class="classeight">item</div>
<div class="classnine">item</div>
<div class="classten">item</div>

II. XSLT 2.0 Solution

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

 <xsl:template match="*[starts-with(name(), 'largeImage')]">
   <xsl:variable name="vIndex">
     <xsl:number count="*[starts-with(name(), 'largeImage')]"
                 level="any" format="w"/>
   </xsl:variable>
   <div class="class{$vIndex}">item</div>
 </xsl:template>
</xsl:stylesheet>

when applied on the same XML document (above), the same correct result is produced:

 <div class="classone">item</div>
 <div class="classtwo">item</div>
 <div class="classthree">item</div>
 <div class="classfour">item</div>
 <div class="classfive">item</div>
 <div class="classsix">item</div>
 <div class="classseven">item</div>
 <div class="classeight">item</div>
 <div class="classnine">item</div>
 <div class="classten">item</div>

Upvotes: 1

Cata
Cata

Reputation: 498

Somewhat more verbose, but you could try something like:

  <xsl:template name="int2str">
    <xsl:param name="val" required="yes"/>
    <xsl:choose>
      <xsl:when test="$val= 1">One</xsl:when>
      <xsl:when test="$val= 2">Two</xsl:when>
      <xsl:when test="$val= 3">Three</xsl:when>
      <xsl:when test="$val= 4">Four</xsl:when>
      <xsl:when test="$val= 5">Five</xsl:when>
      <xsl:when test="$val= 6">Six</xsl:when>
      <xsl:when test="$val= 7">Seven</xsl:when>
      <xsl:when test="$val= 8">Eight</xsl:when>
    </xsl:choose>
  </xsl:template>

you can call it with:

<xsl:call-template name="int2str">
  <xsl:with-param name="val" select="$index"/>
</xsl:call-template>

Upvotes: 0

Martin Honnen
Martin Honnen

Reputation: 167706

Use <div class="class{$numbers/*[position() = $index]}">item</div>. If that does not work then you need to show details of what kind of data your my.library:Split function returns.

Upvotes: 2

Related Questions