tags:

views:

2124

answers:

3

I have some data in an XML element that looks like this:

<item value="category1,category2">Item Name</item>

The bit I'm interested in is the value attribute. I'm able to get the data contained in this attribute into a template which looks like this:

<xsl:template name="RenderValues">
 <xsl:param name="ValueList" />
 <xsl:value-of select="$ValueList" /> <!-- outputs category1,category2-->
</xsl:template>

What I want to do is to process the comma separated values in an efficient manner. What is the best way to render something like the following from inside the RenderValues template?

<a href="x.asp?item=category1">category1</a>
<a href="x.asp?item=category2">category2</a>
+4  A: 

You're looking for the tokenize function:

<xsl:variable name="sampleString">XML,XSLT,XPath,SVG,XPointer</xsl:variable>
<xsl:for-each select="tokenize($sampleString,',')">
    <a>
        <xsl:attribute name="href">
            <xsl:value-of select="concat('x.asp?item=', .)" />
        </xsl:attribute>
    <xsl:value-of select="."/>
    </a>
</xsl:for-each>

http://www.xml.com/pub/a/2003/05/07/tr.html

Craig O
Hi codar, great answer. I took the liberty of adding a link to the offical tokenize function definition and adapting the example in the question. As far as editing goes, this is quite a serious modification, so if you mind, please just revert. I wrote nearly exactly the same text, but you beat me
phihag
I think the modifications add alot. Thank you.
Craig O
+4  A: 

In XSLT 2.0/XPath 2.0 use the standard XPath 2.0 function tokenize().

In XSLT 1.0 one needs either to write a recursively called template or, more conveniently, use the str-split-to-words function/template of the FXSL library.

Here is an example of the latter:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"

>
<!--                                                 -->
   <xsl:import href="strSplit-to-Words.xsl"/>
<!--                                                 -->
   <xsl:output indent="yes" omit-xml-declaration="yes"/>
<!--                                                 -->
    <xsl:template match="/*">
      <xsl:variable name="vwordNodes">
        <xsl:call-template name="str-split-to-words">
          <xsl:with-param name="pStr" select="string(@value)"/>
          <xsl:with-param name="pDelimiters" 
                          select="', &#10;'"/>
        </xsl:call-template>
      </xsl:variable>
<!--                                                 -->
      <xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
    </xsl:template>
<!--                                                 -->
    <xsl:template match="word" priority="10">
      <a href="x.asp?item={.}"><xsl:value-of select="."/></a>
    </xsl:template>
<!--                                                 -->
</xsl:stylesheet>

When the above transformation is applied on the provided XML document:

<item value="category1,category2">Item Name</item>

the wanted result is produced:

<a href="x.asp?item=category1" xmlns:ext="http://exslt.org/common"&gt;category1&lt;/a&gt;
<a href="x.asp?item=category2" xmlns:ext="http://exslt.org/common"&gt;category2&lt;/a&gt;

The pDelimiters parameter of this template allow multiple delimiting characters to be specified. In the above example, any separating character can be either a comma, a space or a new line character.

Dimitre Novatchev
+2  A: 

It's a legitimate question, and the answers provided are good ones. But if you're using XSLT 1.0, and you can't use extension functions, it's simply not possible. XSLT templates produce result tree fragments, and XSLT can only work on node sets. Tokenizing in XSLT requires recursion, which means calling a template, which means producing a data structure that you can't process.

If this limitation applies to you - and really, even if it doesn't - you should, if possible, solve this problem by finding the person who's generating XML whose content has to be parsed again after the DOM has already parsed it and make him or her stop. Putting multiple values in an attribute is just plain wrong.

Robert Rossney
Thanks for your answer. The XML is generated by SharePoint. Specifically it's data that exists in the blog template if you enable multiple category selections for a ports. I can't use EXSLT solutions unless I can control the executions of the .NET code, which is not possible in this case.
BrianLy
It is possible by evaluating every character, e.g. http://stackoverflow.com/questions/1418884/complex-xslt-split
Alex Angas