1

How can I get last position

<contrib contrib-type="author" corresp="no">
  <surname>Duan</surname>
</contrib>
<contrib contrib-type="author" corresp="no">
  <surname>Ding</surname>
</contrib>
<contrib contrib-type="author" corresp="no">
  <surname>Li</surname>
</contrib>
<contrib contrib-type="author" corresp="no">
  <surname>Miao</surname>
</contrib>

This is the XSL

<xsl:template match="contrib">
  <xsl:choose>
    <xsl:when test="position()=last()">
      <xsl:apply-templates />
      <xsl:text> and </xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates/>
      <xsl:text>, </xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

I need this Output: Duan, Ding, Li and Miao

Can anyone tell me what's wrong with it? Thanks in advance.

Shanmugalakshmi
  • 267
  • 2
  • 3
  • 16

2 Answers2

3

You are already using the last() function, but possibly thinking it is not working. Although you have not shown the full XSLT, it is possible you are relying on XSLT's built-in templates to select the contrib elements. In this case, it is selecting nodes in the XML, not just elements, and the position() relates to the nodes just selected, including text nodes. The last contrib element is not the last node because there is a white-space node after the last element.

What you could do is use the strip-space command to remove such white-space nodes.

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*" />

    <xsl:template match="contrib">
      <xsl:choose>
        <xsl:when test="position()=last()">
          <xsl:text> and </xsl:text>
        </xsl:when>
        <xsl:when test="position() > 1">
          <xsl:text>, </xsl:text>
        </xsl:when>
      </xsl:choose>
      <xsl:apply-templates />
    </xsl:template>
</xsl:stylesheet>

However, this may not work if there are other elements in the XML. So, to get around this, you could explicitly select the contrib elements, in which case position would pick up the last one. Try this XSLT too

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*" />

    <xsl:template match="/*">
      <xsl:apply-templates select="contrib" />
    </xsl:template>

    <xsl:template match="contrib">
      <xsl:choose>
        <xsl:when test="position()=last()">
          <xsl:text> and </xsl:text>
        </xsl:when>
        <xsl:when test="position() > 1">
          <xsl:text>, </xsl:text>
        </xsl:when>
      </xsl:choose>
      <xsl:apply-templates />
    </xsl:template>
</xsl:stylesheet>
Tim C
  • 70,053
  • 14
  • 74
  • 93
0

Your code works in a proper context (add a root template) the function position() doesn't work without it.

Check it out:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/"> 
<contribs>
<xsl:apply-templates/>
</contribs>
</xsl:template>

<!-- your template -->

<xsl:template match="contrib">
  <xsl:choose>
    <xsl:when test="position()=last()">
      <xsl:apply-templates />
      <xsl:text> and </xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates/>
      <xsl:text>, </xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

You must also wrap convenient-ly your xml data (multiroot xml is not correct)

<?xml version="1.0" encoding="UTF-8"?>

<doc>

<contrib contrib-type="author" corresp="no">
  <surname>Duan</surname>
</contrib>
<contrib contrib-type="author" corresp="no">
  <surname>Ding</surname>
</contrib>
<contrib contrib-type="author" corresp="no">
  <surname>Li</surname>
</contrib>
<contrib contrib-type="author" corresp="no">
  <surname>Miao</surname>
</contrib>

</doc>

For a nicer result you have to shift the "and" text part after

position()=last()-1

and leave text after position()=last() empty, but this can be easily deduced and executed, isn't it ?

This looks as a poorly prepared homework exercise!!

Gabriel Riba
  • 6,698
  • 2
  • 17
  • 19
  • @michael.hor257k of course I did! (with haskell hxt xslt, and also saxon.jar): java -jar /usr/share/java/saxon.jar contrib.xml contrib2.xsl -- result: Duan, Ding, Li, Miao and – Gabriel Riba May 12 '14 at 09:20
  • The "and" needs to come BEFORE "Miao", INSTEAD OF the comma. – michael.hor257k May 12 '14 at 09:28
  • @michael.hor257k I know that the program can be better, but position() was not working without a root template that was not there – Gabriel Riba May 12 '14 at 09:33