XSLT string replace

345,051

Solution 1

replace isn't available for XSLT 1.0.

Codesling has a template for string-replace you can use as a substitute for the function:

<xsl:template name="string-replace-all">
    <xsl:param name="text" />
    <xsl:param name="replace" />
    <xsl:param name="by" />
    <xsl:choose>
        <xsl:when test="$text = '' or $replace = ''or not($replace)" >
            <!-- Prevent this routine from hanging -->
            <xsl:value-of select="$text" />
        </xsl:when>
        <xsl:when test="contains($text, $replace)">
            <xsl:value-of select="substring-before($text,$replace)" />
            <xsl:value-of select="$by" />
            <xsl:call-template name="string-replace-all">
                <xsl:with-param name="text" select="substring-after($text,$replace)" />
                <xsl:with-param name="replace" select="$replace" />
                <xsl:with-param name="by" select="$by" />
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

invoked as:

<xsl:variable name="newtext">
    <xsl:call-template name="string-replace-all">
        <xsl:with-param name="text" select="$text" />
        <xsl:with-param name="replace" select="a" />
        <xsl:with-param name="by" select="b" />
    </xsl:call-template>
</xsl:variable>

On the other hand, if you literally only need to replace one character with another, you can call translate which has a similar signature. Something like this should work fine:

<xsl:variable name="newtext" select="translate($text,'a','b')"/>

Also, note, in this example, I changed the variable name to "newtext", in XSLT variables are immutable, so you can't do the equivalent of $foo = $foo like you had in your original code.

Solution 2

Here is the XSLT function which will work similar to the String.Replace() function of C#.

This template has the 3 Parameters as below

text :- your main string

replace :- the string which you want to replace

by :- the string which will reply by new string

Below are the Template

<xsl:template name="string-replace-all">
  <xsl:param name="text" />
  <xsl:param name="replace" />
  <xsl:param name="by" />
  <xsl:choose>
    <xsl:when test="contains($text, $replace)">
      <xsl:value-of select="substring-before($text,$replace)" />
      <xsl:value-of select="$by" />
      <xsl:call-template name="string-replace-all">
        <xsl:with-param name="text" select="substring-after($text,$replace)" />
        <xsl:with-param name="replace" select="$replace" />
        <xsl:with-param name="by" select="$by" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$text" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Below sample shows how to call it

<xsl:variable name="myVariable ">
  <xsl:call-template name="string-replace-all">
    <xsl:with-param name="text" select="'This is a {old} text'" />
    <xsl:with-param name="replace" select="'{old}'" />
    <xsl:with-param name="by" select="'New'" />
  </xsl:call-template>
</xsl:variable>

You can also refer the below URL for the details.

Solution 3

Note: In case you wish to use the already-mentioned algo for cases where you need to replace huge number of instances in the source string (e.g. new lines in long text) there is high probability you'll end up with StackOverflowException because of the recursive call.

I resolved this issue thanks to Xalan's (didn't look how to do it in Saxon) built-in Java type embedding:

<xsl:stylesheet version="1.0" exclude-result-prefixes="xalan str"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xalan="http://xml.apache.org/xalan"
                xmlns:str="xalan://java.lang.String"
        >
...
<xsl:value-of select="str:replaceAll(
    str:new(text()),
    $search_string,
    $replace_string)"/>
...
</xsl:stylesheet>

Solution 4

I keep hitting this answer. But none of them list the easiest solution for xsltproc (and probably most XSLT 1.0 processors):

  1. Add the exslt strings name to the stylesheet, i.e.:
<xsl:stylesheet
  version="1.0"
  xmlns:str="http://exslt.org/strings"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  1. Then use it like:
<xsl:value-of select="str:replace(., ' ', '')"/>

Solution 5

You can use the following code when your processor runs on .NET Framework (Not supported on .NET Core or .NET 5) or uses MSXML (as opposed to Java-based or other native processors). It uses msxsl:script.

Make sure to add the namespace xmlns:msxsl="urn:schemas-microsoft-com:xslt" to your root xsl:stylesheet or xsl:transform element.

In addition, bind outlet to any namespace you like, for instance xmlns:outlet = "http://my.functions".

<msxsl:script implements-prefix="outlet" language="javascript">
function replace_str(str_text,str_replace,str_by)
{
     return str_text.replace(str_replace,str_by);
}
</msxsl:script>


<xsl:variable name="newtext" select="outlet:replace_str(string(@oldstring),'me','you')" />
Share:
345,051
Emiel Bruijntjes
Author by

Emiel Bruijntjes

Updated on January 06, 2022

Comments

  • Emiel Bruijntjes
    Emiel Bruijntjes over 2 years

    I don't really know XSL but I need to fix this code, I have reduced it to make it simpler.
    I am getting this error

    Invalid XSLT/XPath function

    on this line

    <xsl:variable name="text" select="replace($text,'a','b')"/>
    

    This is the XSL

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:inm="http://www.inmagic.com/webpublisher/query" version="1.0">
        <xsl:output method="text" encoding="UTF-8" />
    
        <xsl:preserve-space elements="*" />
        <xsl:template match="text()" />
    
        <xsl:template match="mos">
            <xsl:apply-templates />
    
            <xsl:for-each select="mosObj">
              'Notes or subject' 
               <xsl:call-template
                    name="rem-html">
                    <xsl:with-param name="text" select="SBS_ABSTRACT" />
                </xsl:call-template>
            </xsl:for-each>
        </xsl:template>
    
        <xsl:template name="rem-html">
            <xsl:param name="text" />
            <xsl:variable name="text" select="replace($text, 'a', 'b')" />
        </xsl:template>
    </xsl:stylesheet>
    

    Can anyone tell me what's wrong with it?

  • Emiel Bruijntjes
    Emiel Bruijntjes almost 14 years
    Thanks Mark, but now I am getting this error: An unknown XPath extension function was called
  • Mark Elliot
    Mark Elliot almost 14 years
    @aximili, sorry, got XSLT 1.0 and 2.0 confused, edited...should be good to go now.
  • Jakub
    Jakub about 12 years
    This answer is wrong! The replace function in XSLT replaces corresponding SINGLE CHARACTERS, not the whole strings! See for example here: w3schools.com/xpath/xpath_functions.asp
  • Ian Roberts
    Ian Roberts about 11 years
    @Jakub You're thinking of translate, not replace. The replace function in XPath 2.0 treats its second argument as a regular expression and replaces all matches of that expression with the specified replacement string (which may include $n references to capturing groups in the regex). The translate function (in 1.0 and 2.0) is the one that does single-character-for-single-character replacements.
  • DJL
    DJL over 10 years
    shouldn't the 4th line in the example usage be <xsl:with-param name="replace" select="'a'" /> with quotes around the a?
  • Auguste Van Nieuwenhuyzen
    Auguste Van Nieuwenhuyzen over 9 years
    Sorry if I'm being dumb, but I get prefix outlet is not defined or 'xsl:script' cannot be a child of the 'xsl:stylesheet' element. if I change msxsl for my prefix. I'm guessing this is some Microsoft-specific XSLT magic?
  • Auguste Van Nieuwenhuyzen
    Auguste Van Nieuwenhuyzen over 9 years
    Sorry if I'm being dumb but I get: Cannot find a script or an extension object associated with namespace 'xalan://java.lang.String'.
  • Milan Aleksić
    Milan Aleksić over 9 years
    What is your XSLT engine?
  • Milan Aleksić
    Milan Aleksić over 9 years
    My comment was for for most popular Java XSLT 1.0 engine Xalan (xml.apache.org/xalan-j), which supports direct mapping to available types inside available Java classpath; you can't apply my solution for .Net stack
  • Erk
    Erk almost 9 years
    This solution will not be able to handle input with XML/HTML, since a disable-output-escaping-attribute is missing. I guess it must be added manually and if both variants are needed there has to be two templates?
  • Aisah Hamzah
    Aisah Hamzah over 8 years
    @IanGrainger, you can use it with .NET by adding an <msxsl:script> block, which can call any .NET method, library etc. Though .NET also supports the EXSLT extension functions, so you wouldn't need to.
  • Aisah Hamzah
    Aisah Hamzah over 8 years
    @IanGrainger, it is not xsl:script, but msxsl:script, and it has a different namespace (I've updated John's answer).
  • HostMyBus
    HostMyBus over 7 years
    Using xslt 1.0 This post/template worked for me while Mark Elliot's did not.
  • Alain Pannetier
    Alain Pannetier about 7 years
    exslt is also supported in libxslt and therefore in all descendents xsltproc etc...
  • michael.hor257k
    michael.hor257k over 4 years
    The xsltproc on my computer (macOS 10.13) does NOT support the str:replace() function. Neither does any of the other major XSLT 1.0 processors - Xalan, Saxon 6.5 and Microsoft.
  • developer
    developer almost 3 years
    I was getting the exact StackOverflowException error. Thanks for your answer! A quick question, is there anyway to chain multiple replacements using replaceAll(...)?