XSL Multiple Find and Replace Function

I am trying to use the XSL translate () function to create something like a search and replace function as follows:

<xsl:template name="create-id"> <xsl:param name="id" /> <xsl:call-template name="search-and-replace"> <xsl:with-param name="str" select="$id" /> <xsl:with-param name="search">0123456789</xsl:with-param> <xsl:with-param name="replace">abcdefghij</xsl:with-param> </xsl:call-template> </xsl:template> <xsl:template name="search-and-replace"> <xsl:param name="str" /> <xsl:param name="search" /> <xsl:param name="replace" /> <xsl:variable name="newstr" select="translate($str, $search, $replace)" /> <xsl:choose> <xsl:when test="contains($newstr, $search)"> <xsl:call-template name="search-and-replace"> <xsl:with-param name="str" select="$newstr" /> <xsl:with-param name="search" select="$search" /> <xsl:with-param name="replace" select="$replace" /> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$newstr" /> </xsl:otherwise> </xsl:choose> </xsl:template> 

However, something about my logic is wrong here, as it seems to cut off the last character in the returned string. I assume that translate () only replaces the first instance of each character in the string and is not really recursive.

Any thoughts or input will be appreciated.

+4
source share
4 answers

The translate() function can replace only one character with another single character (or an empty character (delete)). Thus, he cannot solve the problem of replacing strings.

Here is the complete XSLT 1.0 solution for the multi-replace problem :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my"> <xsl:output omit-xml-declaration="yes"/> <xsl:strip-space elements="*"/> <my:params xml:space="preserve"> <pattern> <old>&#xA;</old> <new><br/></new> </pattern> <pattern> <old>quick</old> <new>slow</new> </pattern> <pattern> <old>fox</old> <new>elephant</new> </pattern> <pattern> <old>brown</old> <new>white</new> </pattern> </my:params> <xsl:variable name="vPats" select="document('')/*/my:params/*"/> <xsl:template match="text()" name="multiReplace"> <xsl:param name="pText" select="."/> <xsl:param name="pPatterns" select="$vPats"/> <xsl:if test="string-length($pText) >0"> <xsl:variable name="vPat" select= "$vPats[starts-with($pText, old)][1]"/> <xsl:choose> <xsl:when test="not($vPat)"> <xsl:copy-of select="substring($pText,1,1)"/> </xsl:when> <xsl:otherwise> <xsl:copy-of select="$vPat/new/node()"/> </xsl:otherwise> </xsl:choose> <xsl:call-template name="multiReplace"> <xsl:with-param name="pText" select= "substring($pText, 1 + not($vPat) + string-length($vPat/old/node()))"/> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet> 

when this conversion is applied to the following XML document:

 <t>The quick brown fox</t> 

required, the correct result is obtained :

 The slow<br />white elephant 

Explanation

  • A named template is used that calls itself recursively.

  • All pairs with several substitutions -> replaced pairs are provided in one external parameter, which for convenience is indicated here as a built-in element of the global level <my:params> .

  • The recursion takes every single character in the source string (from left to right) and finds the first pattern that starts with that character at that position in the string.

  • Replacement can be not only a string, but also any node. In this particular case, we replace each NL character with <br/> .

+8
source

translate($arg, $mapString, $transString) function definition translate($arg, $mapString, $transString) :

Returns the value of $arg , changed so that each character in the value of $arg that occurs at some position N in the value of $mapString replaced by the character that occurs with position N in the value of $transString .

That is, it does not replace the substring with another string, but rather maps characters to other characters. To replace a substring, use something like

 <xsl:template name="search-and-replace"> <xsl:param name="str"/> <xsl:param name="search"/> <xsl:param name="replace"/> <xsl:choose> <xsl:when test="contains($str, $search)"> <xsl:value-of select="substring-before($str, $search)"/> <xsl:value-of select="$replace"/> <xsl:call-template name="search-and-replace"> <xsl:with-param name="str" select="substring-after($str, $search)"/> <xsl:with-param name="search" select="$search"/> <xsl:with-param name="replace" select="$replace"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$str"/> </xsl:otherwise> </xsl:choose> </xsl:template> 
+3
source

It looks like you will never be true, since you have already replaced all the characters in $ search with $ replace in

 <xsl:variable name="newstr" select="translate($str, $search, $replace)" /> 

in advance.

0
source

str: replace with exslt . It does exactly what you want and is already written and tested by some xslt gurus. An xslt source is also available there .

0
source

Source: https://habr.com/ru/post/1343401/


All Articles