Link to home
Start Free TrialLog in
Avatar of ank5
ank5Flag for India

asked on

XSL - substring between 2 commas

In XSL, I am parsing some valus such as

,b,c
a,b,c

Open in new window

There will always be 2 commas but text before the first comma may or may not be there. I need to display the text at position b (without the commas).

I was trying to using substring-after and substring-before functions for this.

Let's say these values are an XML attribute called ver, so

<xsl:value-of select="ver"/>

Open in new window


would print the values listed above.

Now, If I do

<xsl:value-of select="substring-after(ver, ',')"/>

Open in new window


it gets rid of 'a' and print 'b' and 'c' values (b, c). Then I wrapped this in a substring-before to get rid of 'c'

<xsl:value-of select="substring-before(substring-after(ver, ','), ',')"/>

Open in new window


This works fine when the input being parsed is

a,b,c

Open in new window


it print's b as expected. However, when the input is

,b,c

Open in new window


nothing displays.

How can handle both these scenarios and always print whatever is there at b position.
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

That can't be true
If the input really is what you say it is, your code wil return "b" in both scenarios

try this with any input

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     version="1.0">
    <xsl:variable name="ver1">a,b,c</xsl:variable>
    <xsl:variable name="ver2">,b,c</xsl:variable>
    
    <xsl:template match="/">
        <vers>
            <ver1>
                <xsl:value-of select="substring-before(substring-after($ver1, ','), ',')"/>
            </ver1>
            <ver2>
                <xsl:value-of select="substring-before(substring-after($ver2, ','), ',')"/>
            </ver2>
        </vers>
    </xsl:template>
    
    
</xsl:stylesheet>

Open in new window

Not done but this is start
            <xsl:when test="starts-with(substring-before(substring-after(ver, ','), ',') , ',')">
                <xsl:value-of select="substring-before(substring-after(ver, ','), ',')" />
                <xsl:otherwise>
                    <xsl:value-of select="substring-before(substring-after(ver, ','), ',')" />
                </xsl:otherwise>
            </xsl:when>

Open in new window

Come to think of it, I agree with Geert
@shaun, please note that the xsl:otherwise should be outside the xsl:when, not in it
The test will however always fail because of this statement

There will always be 2 commas
Yup, was still editing until I read your comment :) and decided to abandon until more info is given by OP
agreed,
the choose would be the way ahead if it would not be certain there are two commas
waiting for ank5 now ;-)
Avatar of ank5

ASKER

My apologies, there is a third scenario which I had overlooked and that is causing the problem

,b,c
a,b,c
,b

In all these 3 cases, I just want b to be printed. Sorry for the confusion.
Hi,
you can get around with all sorts of testing
But I would make a named template that recursively scans the string for tokens based on a seperator
And use that to get the index 2
That would be a very generic solution

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:variable name="ver1">a,b,c</xsl:variable>
    <xsl:variable name="ver2">,b,c</xsl:variable>
    <xsl:variable name="ver3">,b</xsl:variable>
    
    <xsl:template match="/">
        <vers>
            <ver1>
                <xsl:call-template name="get-token-by-index">
                    <xsl:with-param name="str" select="$ver1"></xsl:with-param>
                    <xsl:with-param name="index" select="2"/>
                </xsl:call-template>            
            </ver1>
            <ver2>
                <xsl:call-template name="get-token-by-index">
                    <xsl:with-param name="str" select="$ver2"></xsl:with-param>
                    <xsl:with-param name="index" select="2"/>
                </xsl:call-template>            
            </ver2>
            <ver3>
                <xsl:call-template name="get-token-by-index">
                    <xsl:with-param name="str" select="$ver3"></xsl:with-param>
                    <xsl:with-param name="index" select="2"/>
                </xsl:call-template>            
            </ver3>
        </vers>
    </xsl:template>
    
    <xsl:template name="get-token-by-index">
        <xsl:param name="str"/>
        <xsl:param name="sep" select="','"/>
        <xsl:param name="index" select="1"/>
        <xsl:choose>
            <xsl:when test="contains($str, $sep) and $index > 1">
                <xsl:call-template name="get-token-by-index">
                    <xsl:with-param name="str" select="substring-after($str, $sep)"/>
                    <xsl:with-param name="index" select="$index - 1"/>
                    <xsl:with-param name="sep" select="$sep"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="contains($str, $sep)">
                <xsl:value-of select="substring-before($str, $sep)"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$str"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    
    
</xsl:stylesheet>

Open in new window

<xsl:template name="get-token-by-index">
is the template you need to copy to your code

and then use it as in the examples above it
ASKER CERTIFIED SOLUTION
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of ank5

ASKER

Thank you very much for the solution, it works great.

My apologies for the delay in closing the question.