Bugude
Bugude

Reputation: 311

XSL: Construct an Array from a Delimited String

I have a string in which data is separated by a delimiter like "|" and is present in a variable. I would like to create an array in the XSL by dividing the above string based on the delimiter and would like to access the same in the in a for loop.

Please help me in this regard. Please also let me know if anyone need any more information.

String is "Test1|Test2|Test3|Test4" and would like to get a variable TEMP which would be an array of data from the string and would like to access as TEMP[index].

I have tried to use the tokenize function after the inputs from the forum members to get the values from the string but was not successful. I am not getting the string values in the loop.

<xsl:variable name="temp" xmlns:str="http://exslt.org/strings" select="str:tokenize(normalize-space(' Test1$,$Test2$,$Test3$,$Test4 '),'$,$')"/>
<xsl:for-each xmlns:str="http://exslt.org/strings" select="str:split(normalize-space(' 1$,$2$,$3$,$4$,$5$,$6 '),'$,$')">
    <xsl:variable name="index" select="position()"/>
    <xsl:value-of select="$temp[$index]"/>
</xsl:for-each>

Regards, Lakshman

Upvotes: 16

Views: 31201

Answers (4)

Josue Guol
Josue Guol

Reputation: 31

I try this, I modified the "Dimitre Novatchev" code, and works for me: (please excuse me for my English)

<?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:template match="/*">
        <xsl:variable name="_keywords">
            <xsl:call-template name="split-to-values">
                <xsl:with-param name="_text" select="Keywords-comma-separated"/>
            </xsl:call-template>
        </xsl:variable>

        <xsl:for-each select="msxsl:node-set($_keywords)/value">
            <xsl:variable name="_keyword" select="."/>
            <!-- ANY CODE -->
        </xsl:for-each>
</xsl:template>

<xsl:template match="text()" name="split-to-values">
    <xsl:param name="_text"/>

    <xsl:if test="string-length($_text)">
        <xsl:variable name="_value" select="substring-before($_text, ',')"/>
        <xsl:variable name="_next" select="substring-after($_text, ',')"/>
        <value>
            <xsl:value-of select="$_value"/>
        </value>

        <xsl:call-template name="split-to-values">
            <xsl:with-param name="_text" select="$_next"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

I hope it helps you

@Bugude ask

I have a string in which data is separated by a delimiter like "|" and is present in a variable. I would like to create an array in the XSL by dividing the above string based on the delimiter and would like to access the same in the in a for loop

Then the string may be: "alfa,beta,gama,delta"

The delimiter is presented in a variable:

<xsl:variable name="_delimiter">,</xsl:variable>

I modified the template as:

<xsl:template match="text()" name="split-to-values">
    <xsl:param name="_text"/>
    <xsl:param name="_delimiter"/>

    <xsl:if test="string-length($_text)">
        <xsl:variable name="_value" select="substring-before($_text, $_delimiter)"/>
        <xsl:variable name="_next" select="substring-after($_text, $_delimiter)"/>
        <value>
            <xsl:value-of select="$_value"/>
        </value>

        <xsl:call-template name="split-to-values">
            <xsl:with-param name="_text" select="$_next"/>
            <xsl:with-param name="_delimiter" select="$_delimiter"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

And we can access to the nodes as similar an array with a for

<xsl:template match="/*">
        <!-- _keyword as set a node -->
        <xsl:variable name="_keywords">
            <xsl:call-template name="split-to-values">
                <xsl:with-param name="_text" select="'alfa,beta,gama,delta'"/>
                <xsl:with-param name="_delimiter" select="$_delimiter"/>
            </xsl:call-template>
        </xsl:variable>

        <xsl:for-each select="msxsl:node-set($_keywords)/value">
            <xsl:variable name="_keyword" select="."/>
            <!-- ANY CODE -->
            <xsl:value-of select="$_keyword"/>
        </xsl:for-each>
</xsl:template>

Upvotes: 3

Vincent Biragnet
Vincent Biragnet

Reputation: 2998

It won't be an array but a sequence, and you must have a XSLT 2.0 processor. You can use the tokenize() function :

<xsl:variable name="temp" as="xs:string*" select="tokenize('Test1|Test2|Test3|Test4','\|')"/>

You can also pass a string variable as first argument of tokenize.

Then you use :

<xsl:value-of select="$temp[$index]"/>

EDIT : achieve this in xslt 1.0 is not possible unless you use some extension.

Upvotes: 11

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243549

String is "Test1|Test2|Test3|Test4" and would like to get a variable TEMP which would be an array of data from the string and would like to access as TEMP[index].

+1 for a good question.

This XSLT 1.0 transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vrtfTokens">
  <xsl:apply-templates/>
 </xsl:variable>

 <xsl:variable name="vTokens" select=
   "ext:node-set($vrtfTokens)/*"/>
 <xsl:template match="/">
  <xsl:for-each select=
   "document('')//node()[not(position() > count($vTokens))]
   ">
   <xsl:variable name="vPos" select="position()"/>
    <xsl:copy-of select="$vTokens[$vPos+0]"/>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="text()" name="split">
  <xsl:param name="pText" select="."/>

  <xsl:if test="string-length($pText)">
   <xsl:variable name="vToken" select=
    "substring-before(concat($pText,'|'), '|')"/>
   <s><xsl:value-of select="$vToken"/></s>

   <xsl:call-template name="split">
    <xsl:with-param name="pText" select=
    "substring-after($pText, '|')"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

when applied on this XML document:

<t>Test1|Test2|Test3|Test4</t>

creates a variable vTokens that contains elements named s, each of which has as its only text child a token from the '|'-delimited string "Test1|Test2|Test3|Test4".

Then the transformation outputs each of these s elements using an "index".

The wanted, correct result is produced:

<s>Test1</s>
<s>Test2</s>
<s>Test3</s>
<s>Test4</s>

In case we want just the tokens (strings) themselves, we'd use:

string($vTokens[someIndex])

Upvotes: 17

Martin Honnen
Martin Honnen

Reputation: 167716

That task is called tokenizing, in XSLT 2.0 you can use tokenize('Test1|Test2|Test3|Test4', '\|'), with XSLT 1.0 you can use an extension like http://www.exslt.org/str/functions/tokenize/index.html.

Upvotes: 3

Related Questions