I need to iterate over the characters in a string to build an XML structure.
Currently, I am doing this :
<xsl:template name="verticalize">
<xsl:param name="text">Some text</xsl:param>
<xsl:for-each select="tokenize(replace(replace($text,'(.)','$1\\n'),'\\n$',''),'\\n')">
<xsl:element name="para">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:template>
This produces something like :
<para>S</para>
<para>o</para>
<para>m</para>
<para>e</para>
<para> </para>
<para>t</para>
<para>e</para>
<para>x</para>
<para>t</para>
This works fine with Xpath 2.0. But I need to apply the same treatment in a XPath 1.0 environment, where the replace()
method is not available.
Do you know a way to achieve this ?
The <xsl:text> element is used to write literal text to the output. Tip: This element may contain literal text, entity references, and #PCDATA.
You can format your XSLT stylesheet to go to a specific node, and then loop through the given node set. You create an XSLT loop with the <xsl:for-each> tag. The value of the select attribute in this tag is an XPath expression that allows you to specify the data element to loop through.
If the argument is a boolean value, the value true is converted to the number 1 ; the value false is converted to the number 0 . If the argument is a node-set, the node-set is converted to a string as if it were passed to the string() function, then that string is converted to a number like any other string.
contains() Function — Determines if the first argument string contains the second.
<xsl:template name="letters">
<xsl:param name="text" select="'Some text'" />
<xsl:if test="$text != ''">
<xsl:variable name="letter" select="substring($text, 1, 1)" />
<para><xsl:value-of select="$letter" /></para>
<xsl:call-template name="letters">
<xsl:with-param name="text" select="substring-after($text, $letter)" />
</xsl:call-template>
</xsl:if>
</xsl:template>
If the string length is not huge, you can use a recursively called template to achieve this, passing the index of the character to be processed as parameter into the template.
Like so:
<xsl:template name="verticalize">
<xsl:param name="text">Some text</xsl:param>
<xsl:param name="index" select="1" />
<xsl:if test="string-length($text) >= $index">
<xsl:element name="para">
<xsl:value-of select="substring($text, $index, 1)"/>
</xsl:element>
<xsl:call-template name="verticalize">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="index" select="$index+1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
If the string is longer than that, you can use a similar approach but with a divide-and-conquer algorithm, so that you have a maximum recursion depth of log2(string-length), like so:
<xsl:template name="verticalize">
<xsl:param name="text">Some text</xsl:param>
<xsl:param name="left" select="1" />
<xsl:param name="right" select="string-length($text)" />
<xsl:choose>
<xsl:when test="$left = $right">
<xsl:element name="para">
<xsl:value-of select="substring($text, $left, 1)"/>
</xsl:element>
</xsl:when>
<xsl:when test="$left < $right">
<xsl:variable name="middle" select="floor(($left+$right) div 2)" />
<xsl:call-template name="verticalize">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="left" select="$left" />
<xsl:with-param name="right" select="$middle" />
</xsl:call-template>
<xsl:call-template name="verticalize">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="left" select="$middle+1" />
<xsl:with-param name="right" select="$right" />
</xsl:call-template>
</xsl:when>
</xsl:choose>
</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