16

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

3
  • FYI, XSLT doesn't have arrays. In XSLT 1.0 / EXSLT, the tokenize() extension function returns a node set or a result tree fragment (which can be turned into a node set using another extension function, node-set()). This node set can be accessed by index as you specified, because the elements representing tokens map one-to-one to children of a parent node, in order. Commented Nov 29, 2011 at 19:28
  • @LarsH: Yes, but what the OP wants can be modeled in XSLT 1.0 by a node-set containing elements each of which contains a token -- see my answer. Commented Nov 30, 2011 at 2:22
  • @Dimitre: yeah, I was alluding to that in my comment. There were already good answers posted describing how to do that when I wrote my comment. I just wanted to add a bit about 'array' terminology. Commented Nov 30, 2011 at 19:00

4 Answers 4

17

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])
Sign up to request clarification or add additional context in comments.

1 Comment

@Bugude: You are welcome. Probably you need to be aware that saying "Thank you" at SO is officially done by accepting the best answer (by clicking the check-mark next to it -- and you can do this now) and by upvoting any answer (by clicking its ^ arrow -- something you'll be allowed to do when you have accumulated 50 points yourself).
11

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.

3 Comments

Thank you Vincent...This help and worked when I am using individually and using XSLT 2.0. Unfortunately, the application is using old version, is there anyway that this can be done in version 1.0
Unfortunately, it's not in the xpath 1.0 standard and you have to use an exslt extension as Martin pointed out.
@VincentBiragnet: Your statement that one must use an XSLT 2.0 processor in order to implement the OP's requirements, is not true. See my XSLT 1.0 solution.
3

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.

2 Comments

Thanks everyone. I have tried to use the tokenize function 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="exslt.org/strings" select="str:tokenize(normalize-space(' Test1$,$Test2$,$Test3$,$Test4 '),'$,$')"/> <xsl:for-each xmlns:str="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>
The above code that is mentioned is used to populate a table in a workbook. The values that are obtained in the for loop will go in each row of a table in the workbook (Excel)
3

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>

1 Comment

While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.