I am attempting 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 appears to be stripping off the last character in the returned string. My guess is that translate() is only replacing the first instance of each character in the string and is not truly recursive.
Any thoughts or input would be appreciated.
The translate()
function can only replace a single character with another single character (or with the empty character (delete)). Thus it cannot solve the problem of string replacement.
Here is a complete XSLT 1.0 solution to the multiple-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>
</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 transformation is applied on the following XML document:
<t>The quick
brown fox</t>
the wanted, correct result is produced:
The slow<br />white elephant
Explanation:
A named template that calls itself recursively is used.
All multiple replacement pattern --> replacement pairs are provided in a single external parameter, which for convenience here is specified inline as the global-level element <my:params>
.
The recursion takes every single character in the source string (from left to right) and finds the first pattern that starts with this character at this position in the string.
The replacement can be not only a string but also any node. In this specific case we are replacing every NL character with a <br/>
element.
The definition of the translate($arg, $mapString, $transString)
function is:
Returns the value of
$arg
modified so that every character in the value of$arg
that occurs at some position N in the value of$mapString
has been replaced by the character that occurs at position N in the value of$transString
.
That is, it does not replace a substring with another string, but rather maps characters to other characters. For substring replacement, 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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With