I have an input string which has csv values. Eg., 1,2,3 I would need to separate each values and assign to target node in for-each loop.
I got this below template that splits the input string based on delimiter. How can I assign each of the delimited values to the target element in for-each loop.
<xsl:template name="output-tokens">
<xsl:param name="list"/>
<xsl:param name="delimiter"/>
<xsl:variable name="newlist">
<xsl:choose>
<xsl:when test="contains($list, $delimiter)">
<xsl:value-of select="normalize-space($list)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(normalize-space($list), $delimiter)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="first" select="substring-before($newlist, $delimiter)"/>
<xsl:variable name="remaining"
select="substring-after($newlist, $delimiter)"/>
<xsl:variable name="count" select="position()"/>
<num>
<xsl:value-of select="$first"/>
</num>
<xsl:if test="$remaining">
<xsl:call-template name="output-tokens">
<xsl:with-param name="list" select="$remaining"/>
<xsl:with-param name="delimiter">
<xsl:value-of select="$delimiter"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
Input xml:
<out1:AvailableDates>
<out1:AvailableDate>15/12/2011,16/12/2011,19/12/2011,20/12/2011,21/12/2011</out1:AvailableDate>
</out1:AvailableDates>
Expected Output:
<tns:AvailableDates>
<tns:AvailableDate>15/12/2011</tns:AvailableDate>
<tns:AvailableDate>16/12/2011</tns:AvailableDate>
<tns:AvailableDate>120/12/2011</tns:AvailableDate>
</tns:AvailableDates>
Here is a complete and short, true XSLT 1.0 solution:
when applied on the provided XML document (corrected to be made well-formed):
the wanted, correct result is produced:
With XSLT 2.0 you can use
tokenize(string, separator)
function instead of named template.And this xsl:
gives following result:
Update:
With Xslt 2.0 processor under backward compatibility mode following template gives the same result:
For Xslt 1.0 - it is not possible simple (with standard functions) access to nodes via variable - see @Dimitre Novatchev answer XSLT 1.0 - Create node set and pass as a parameter
For this purpose XSLT 1.0 processors contains extension function:
node-set(...)
For Saxon 6.5
node-set()
function is defined inhttp://icl.com/saxon
namespaceSo in the case of XSLT 1.0 processors solution would be:
Thanks @Dimitre Novatchev to correct me and his answer about accessing node sets from variable.
Personally, I prefer this variant based on custom extension functions. The method is compact and clean, and works fine in XSLT 1.0 (at least with XALAN 2.7 as embedded in any recent JVM).
1) declare a class with a static method returning a org.w3c.dom.Node
This function splits an input string on a regex pattern and creates a document fragment of the kind <list><Item>A</Item><Item>B</Item><Item>C</Item></List>. The regex is matched in sequence, each match yielding an Item element whose value is composed from the capturing groups (some possibly empty) inside each regex match. This allows to get rid from delimiters and other syntax chars.
For instance, to split a comma-separated list like
" A, B ,, C"
, skip empty values, and trim extra spaces (hence get the above Node list), use a regex like'\s*([^,]+?)\s*(?:,|$)'
- a mind twisting one! If instead you want to split the input text by a fixed size (here 10 chars) with the last Item taking whatever remains, use a regex like'(.{10}|.+)'
- love it!You can then use the function in XSLT 1.0 as follows (quite compact!):
Executed on a template match yielding the input fragment
<Detail><CsvText>a, b ,c </CsvText></Detail>
you'll generate<Loop>a</Loop><Loop>b</Loop><Loop>c</Loop>
The trick is not forgetting to follow the function call that generates the Node/Item by the XPath "/Item" (or "/*") as you shall note, so that a Node sequence is returned into the for-each loop.