XSL substring and indexOf

136,869

Solution 1

It's not clear exactly what you want to do with the index of a substring [update: it is clearer now - thanks] but you may be able to use the function substring-after or substring-before:

substring-before('My name is Fred', 'Fred')

returns 'My name is '.

If you need more detailed control, the substring() function can take two or three arguments: string, starting-index, length. Omit length to get the whole rest of the string.

There is no index-of() function for strings in XPath (only for sequences, in XPath 2.0). You can use string-length(substring-before($string, $substring))+1 if you specifically need the position.

There is also contains($string, $substring). These are all documented here. In XPath 2.0 you can use regular expression matching.

(XSLT mostly uses XPath for selecting nodes and processing values, so this is actually more of an XPath question. I tagged it thus.)

Solution 2

Here is some one liner xpath 1.0 expressions for IndexOf( $text, $searchString ):

If you need the position of the FIRST character of the sought string, or 0 if it is not present:

contains($text,$searchString)*(1 + string-length(substring-before($text,$searchString)))

If you need the position of the first character AFTER the found string, or 0 if it is not present:

contains($text,$searchString)*(1 + string-length(substring-before($text,$searchString)) + string-length($searchString))

Alternatively if you need the position of the first character AFTER the found string, or length+1 if it is not present:

1 + string-length($right) - string-length(substring-after($right,$searchString))

That should cover most cases that you need.

Note: The multiplication by contains( ... ) causes the true or false result of the contains( ... ) function to be converted to 1 or 0, which elegantly provides the "0 when not found" part of the logic.

Solution 3

I want to select the text of a string that is located after the occurrence of substring

You could use:

substring-after($string,$match)

If you want a subtring of the above with some length then use:

substring(substring-after($string,$match),1,$length)

But problems begin if there is no ocurrence of the matching substring... So, if you want a substring with specific length located after the occurrence of a substring, or from the whole string if there is no match, you could use:

substring(substring-after($string,substring-before($string,$match)),
          string-length($match) * contains($string,$match) + 1,
          $length) 

Solution 4

The following is the complete example containing both XML and XSLT where substring-before and substring-after are used

<?xml version="1.0" encoding="UTF-8"?>
<persons name="Group_SOEM">
    <person>
        <first>Joe Smith</first>
        <last>Joe Smith</last>
        <address>123 Main St, Anycity</address>
    </person>
</persons>    

The following is XSLT which changes value of first/last name by separating the value by space so that after applying this XSL the first name element will have value "Joe" and last "Smith".

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="first">
    <first>
        <xsl:value-of select="substring-before(.,' ')" />
    </first>
</xsl:template> 
<xsl:template match="last">
    <last>
        <xsl:value-of select="substring-after(.,' ')" />
    </last>
</xsl:template> 
<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>   

Solution 5

There is a substring function in XSLT. Example here.

Share:
136,869

Related videos on Youtube

Carlos Blanco
Author by

Carlos Blanco

Updated on September 03, 2020

Comments

  • Carlos Blanco
    Carlos Blanco almost 4 years

    I'm new to XSLT. I wonder if it is possible to select a substring of an item. I'm trying to parse an RSS feed. The description value has more text than what I want to show. I'd like to get a subtring of it based on the index of some substring. Basically, I want to show the result of a substring call passing indxOf('some_substring') and a length as parameters. Is this possible?

    From comments:

    I want to select the text of a string that is located after the occurrence of substring

    • Admin
      Admin over 13 years
      Your question isn't clear. Do you want a substring of some length beginning from some matched substring?
    • Carlos Blanco
      Carlos Blanco over 13 years
      I want to select the text of a string that is located after the occurrence of substring.
  • Carlos Blanco
    Carlos Blanco over 13 years
    substring-after looks like the one I need. I just need to figure out how to use it. I tried "<span><xsl:value-of select="substring-after(rss/channel/item/description,"Reusab‌​le HTML:") disable-output-escaping="yes"/></span>". Doesn't work.
  • Carlos Blanco
    Carlos Blanco over 13 years
    Got it. <span><xsl:value-of select="substring-after(description,'Reusable HTML:') disable-output-escaping="yes"/></span>
  • LarsH
    LarsH over 13 years
    @tou: why are you using disable-output-escaping? It might be appropriate, but more often it's a sign of using a chisel as a screwdriver, or some such. If a character is going to be escaped in the output, it's probably because it needs to be escaped. Unless you're trying to migrate markup from one level to another (e.g. trying to parse a raw string of HTML into a DOM).
  • Dimitre Novatchev
    Dimitre Novatchev over 13 years
    @LarsH: There is index-of() function in XPath 2, only it operates on sequences. One can generalize contains() with index-of() and subsequence()
  • LarsH
    LarsH over 13 years
    @Dimitre: thanks for pointing that out. I was only looking under "Functions on string values" - should have searched! Will edit my answer.
  • Carlos Blanco
    Carlos Blanco over 13 years
    I use disable-output-escaping="yes" because I want to render the HTML in the page. SharePoint stuff ;)
  • Sprotty
    Sprotty almost 10 years
    This solution contains a bug, the results for string-length(substring-before('abc', 'a'))+1 = 1 the results for string-length(substring-before('abc', 'z'))+1 = 1 A full solution would be if (contains('abc', 'a')) then (string-length(substring-before('abc', 'a'))+1) else (-1) (Also just noticed the answer by user357812!)
  • LarsH
    LarsH almost 10 years
    @Sprotty: your point is valid. And yes, Alejandro's (user357812's) post covers this aspect of the situation. Whether it's actually a bug or just a limitation depends on the range of possible inputs the user may have.
  • silentsurfer
    silentsurfer over 6 years
    I believe there is an error in the implementation of your second formula. It should read: contains($text,$searchString)*(1 + string-length(substring-before($text,$searchString)) + string-length($searchString)) (the last parenthesis is in the wrong place). But thanks anyway, this really helped me!
  • MrWatson
    MrWatson over 6 years
    Thank you @silentsurfer - well spotted!