Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT 1.0 : Iterate over characters in a string

Tags:

xslt

xpath

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 ?

like image 807
glmxndr Avatar asked May 06 '10 14:05

glmxndr


People also ask

What is text () in XSLT?

The <xsl:text> element is used to write literal text to the output. Tip: This element may contain literal text, entity references, and #PCDATA.

How to Iterate in XSLT?

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.

What is Number () in XSLT?

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.

Can we use contains in XSLT?

contains() Function — Determines if the first argument string contains the second.


2 Answers

<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>
like image 155
Tomalak Avatar answered Sep 16 '22 14:09

Tomalak


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) &gt;= $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 &lt; $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>
like image 23
Lucero Avatar answered Sep 18 '22 14:09

Lucero