I am trying to emulate StringBuilder behavior in an XSL. Is there a way to do this. It seems pretty hard given the fact that XSLT is a functional programming language
Have a look at the concat()
and string-join()
functions, maybe that's what you are after.
You can use all available standard XPath 2.0 string functions, such as concat()
, substring()
, substring-before()
, substring-after()
, string-join()
, ..., etc.
However, in case you need a very fast implementation of strings (even faster than the .NET string class) you'll probably be interested in the C# implementation of the finger-tree data structure and the extension functions I provided for the Saxon XSLT processor that wrap the finger-tree-based string.
You can get the accumulting concats quite simply with just a little bit of recursion if you're looking at a node-set (so long as you can construct the xpath to find the node-set), doing this so you can add arbitrary bits and pieces in and out of the flow it starts getting messy.
Try this for starters (does join as well):
<xsl:template match="/">
<xsl:variable name="s">
<xsl:call-template name="stringbuilder">
<xsl:with-param name="data" select="*" /><!-- your path here -->
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$s" /><!-- now contains a big concat string -->
</xsl:template>
<xsl:template name="stringbuilder">
<xsl:param name="data"/>
<xsl:param name="join" select="''"/>
<xsl:for-each select="$data/*">
<xsl:choose>
<xsl:when test="not(position()=1)">
<xsl:value-of select="concat($join,child::text())"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="child::text()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
All manner of extensions to that may be required: perhaps you want to trim, perhaps you want to tunnel through hierarchies as well. I'm not sure a bulletproof general solution exists.