I'm writing an XSLT transformation (for XSL-FO), and need to repeat something for each letter in a string value, for example:
If string is stored in MyData/MyValue
string (e.g. MyData.MyValue = "something"), I need an for-each like this one:
<xsl:for-each select="MyData/MyValue"> <!-- What goes here to iterate through letters? -->
<someTags>
<xsl:value-of select="Letter" /> <!-- What goes here to output current letter? -->
</someTags>
</xsl:for-each>
Any ideas?
Several XSLT 1.0 solutions have been posted (since that is what the original poster needed). For comparison, though, here is how this could be done in XSLT 2.0 using xsl:analyze-string:
<xsl:analyze-string select="MyData/MyValue" regex=".">
<xsl:matching-substring>
<someTags>
<xsl:value-of select="."/>
</someTags>
</xsl:matching-substring>
</xsl:analyze-string>
you could use a call template and pass parameters, then use recursion to call the template untill there are no characters left.
example added below.
on this xml
<?xml version="1.0" encoding="utf-8"?>
<data>
<node>something</node>
</data>
and this xslt
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="data/node">
<xsl:call-template name="for-each-character">
<xsl:with-param name="data" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="for-each-character">
<xsl:param name="data"/>
<xsl:if test="string-length($data) > 0">
<someTags>
<xsl:value-of select="substring($data,1,1)"/>
</someTags>
<xsl:call-template name="for-each-character">
<xsl:with-param name="data" select="substring($data,2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
you will then be able to manipulate in the if statement to do further stuff!
You can try this dirt-ugly hack that has proven to work time and again:
<xsl:for-each select="//*[position() <= string-length(MyData/MyValue)]">
<someTags>
<xsl:value-of select="substring(MyData/MyValue, position(), 1)"/>
</someTags>
</xsl:for-each>
This will work if //*
matches more nodes than the number of characters in your string... Of course, this would also deserve the odd line of comment for the poor fellow reading your code afterwards... ;-)
Note: I know there are XSLT purists out there. But when you need to get the job done and don't care much about the hyper-verbosity of XSLT, then sometimes these tricks are awesome! IMO
Note also: I have raised a performance question here, to see if iteration or recursion performs better: XSLT iteration or recursion performance
I'm not sure about the feasibility of the iteration. You can use recursion obsviously as shown in other answers. This is my proposal (not much different from the others apart the fact I'm using template match patterns and not named templates):
<xsl:template match="MyData/MyValue">
<xsl:param name="sub" select="."/>
<xsl:variable name="subsub" select="substring($sub,1,1)"/>
<xsl:if test="boolean($subsub)">
<someTags>
<xsl:value-of select="$subsub"/>
</someTags>
<xsl:apply-templates select="self::node()">
<xsl:with-param name="sub" select="substring($sub,2)"/>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With