Search code examples
xmlxsltdelimitertokenize

xslt split by delimiter


I cant use tokenize (xslt 2.0)

I have a problem. I need to split a value "Test street\nStreet 2\nStreet3" based on a delimiter "\n" to produce

        <address1>Test street</address3>
        <address2>Street 2</address3>
        <address3>Street3</address3>

I have this so far:

<xsl:call-template name="tokenize">
    <xsl:with-param name="text" select="ns0:sObject/ens:BillingStreet"/>
</xsl:call-template>

<xsl:template name="tokenize">
    <xsl:param name="text"/>
    <xsl:param name="separator" select="'\n'"/>
    <xsl:choose>
        <xsl:when test="not(contains($text, $separator))">
            <Address>
                <xsl:value-of select="normalize-space($text)"/>
            </Address>
        </xsl:when>
        <xsl:otherwise>
            <Address>
                <xsl:value-of select="normalize-space(substring-before($text, $separator))"/>
            </Address>
            <xsl:call-template name="tokenize">
                <xsl:with-param name="text" select="substring-after($text, $separator)"/>
            </xsl:call-template>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

It gives me this:

        <Aaddress>Test street</Address>
        <Aaddress>Street 2</Address>
        <Aaddress>Street3</Address>

I tried using the position() but that wont work:

<xsl:template name="tokenize">
    <xsl:param name="text"/>
    <xsl:param name="separator" select="'\n'"/>
    <xsl:choose>
        <xsl:when test="not(contains($text, $separator))">
            <xsl:element name="Address{position()}">
                <xsl:value-of select="normalize-space($text)"/>
            </xsl:element>
        </xsl:when>
        <xsl:otherwise>
            <xsl:element name="Address{position()}">
                <xsl:value-of select="normalize-space(substring-before($text, $separator))"/>
            </xsl:element>
            <xsl:call-template name="tokenize">
                <xsl:with-param name="text" select="substring-after($text, $separator)"/>
            </xsl:call-template>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

The above gives me:

        <Address1>Test street</Address1>
        <Address1>Street 2</Address1>
        <Address1>Street3</Address1>

Again, what I am looking is:

        <Address1>Test street</Address1>
        <Address2>Street 2</Address2>
        <Address3>Street3</Address3>

Any idea?

Thanks


Solution

  • position() is not really relevant here, as it returns the position of the currently selected node.

    You will have to keep track of what iteration your recursive template is on by means of another parameter:

    <xsl:template name="tokenize">
        <xsl:param name="text"/>
        <xsl:param name="separator" select="'\n'"/>
        <xsl:param name="pos" select="1" ></xsl:param>
        <xsl:choose>
            <xsl:when test="not(contains($text, $separator))">
                <xsl:element name="Address{$pos}">
                    <xsl:value-of select="normalize-space($text)"/>
                </xsl:element>
            </xsl:when>
            <xsl:otherwise>
                <xsl:element name="Address{$pos}">
                    <xsl:value-of select="normalize-space(substring-before($text, $separator))"/>
                </xsl:element>
                <xsl:call-template name="tokenize">
                    <xsl:with-param name="text" select="substring-after($text, $separator)"/>
                    <xsl:with-param name="pos" select="$pos + 1" />
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template> 
    

    Note, you can slightly simplify your template, to avoid a bit of code duplication with creating the Address element; like so:

    <xsl:template name="tokenize">
        <xsl:param name="text"/>
        <xsl:param name="separator" select="'\n'"/>
        <xsl:param name="pos" select="1" ></xsl:param>
    
        <xsl:element name="Address{$pos}">
            <xsl:value-of select="normalize-space(substring-before(concat($text, $separator), $separator))"/>
        </xsl:element>
        <xsl:if test="contains($text, $separator)" >
            <xsl:call-template name="tokenize">
                <xsl:with-param name="text" select="substring-after($text, $separator)"/>
                <xsl:with-param name="pos" select="$pos + 1" />
            </xsl:call-template>
        </xsl:if>
    </xsl:template>