Alex Rivera | Logout

How do I generate a comma-separated list with XSLT/XPath?

Asked 2009-03-20T15:16:53.767
37

Given this XML data:

<root>
  <item>apple</item>
  <item>orange</item>
  <item>banana</item>
</root>

I can use this XSLT markup:

...
<xsl:for-each select="root/item">
  <xsl:value-of select="."/>,
</xsl:for-each>
...

to get this result:

apple, orange, banana,

but how do I produce a list where the last comma is not present? I assume it can be done doing something along the lines of:

...
<xsl:for-each select="root/item">
  <xsl:value-of select="."/>
  <xsl:if test="...">,</xsl:if>
</xsl:for-each>
...

but what should the test expression be?

I need some way to figure out how long the list is and where I currently am in the list, or, alternatively, if I am currently processing the last element in the list (which means I don't care how long it is or what the current position is).

Edit
Report

2 Answers

15

For an XSLT 2.0 option, you can use the separator attribute on xsl:value-of.

This xsl:value-of:

<xsl:value-of select="/root/item" separator=", "/>

would produce this output:

apple, orange, banana

You could also use more than just a comma for a separator. For example, this:

<xsl:text>'</xsl:text>
<xsl:value-of select="/root/item" separator="', '"/>
<xsl:text>'</xsl:text>

Would produce the following output:

'apple', 'orange', 'banana'

Another XSLT 2.0 option is string-join()...

<xsl:value-of select="string-join(/*/item,', ')"/>
answered 2012-07-10T15:45:21.310
7

Robert gave the classis not(position() = last()) answer. This requires you to process the whole current node list to get context size, and in large input documents this might make the conversion consume more memory. Therefore, I normally invert the test to be the first thing

<xsl:for-each select="*">
  <xsl:if test="not(position() = 1)>, </xsl:if>
  <xsl:value-of select="."/>   
</xsl:for-each>
answered 2009-03-22T17:26:55.810

Your Answer