How to replace multiple text substrings in XSLT 1

With XSLT 1.0, XSLT 2.0 regex methods are generally not available. Is there any non-regex way to replace multiple fields in a node in an XML source document, for example for conversion:

<?xml version="1.0" encoding="utf-8"?> <xliff xmlns:xliff="urn:oasis:names:tc:xliff:document:1.1" version="1.1"> <file> <source>abc [[field1]] def [[field2]] ghi</source> </file> </xliff> 

in

 <?xml version="1.0" encoding="utf-8"?> <xliff xmlns:xliff="urn:oasis:names:tc:xliff:document:1.1" version="1.1"> <file> <source>abc F def F ghi</source> </file> </xliff> 
+4
source share
4 answers

I. XSLT 1.0 Solution:

This conversion is :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:param name="pTargetStart" select="'[['"/> <xsl:param name="pTargetEnd" select="']]'"/> <xsl:param name="pReplacement" select="'F'"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="source/text()" name="replace"> <xsl:param name="pText" select="."/> <xsl:param name="pTargetStart" select="$pTargetStart"/> <xsl:param name="pTargetEnd" select="$pTargetEnd"/> <xsl:param name="pRep" select="$pReplacement"/> <xsl:choose> <xsl:when test= "not(contains($pText, $pTargetStart) and contains($pText, $pTargetEnd) ) or not(contains(substring-after($pText, $pTargetStart), $pTargetEnd ) ) "> <xsl:value-of select="$pText"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="substring-before($pText, $pTargetStart)"/> <xsl:value-of select="$pRep"/> <xsl:variable name="vremText" select= "substring-after(substring-after($pText, $pTargetStart), $pTargetEnd )"/> <xsl:call-template name="replace"> <xsl:with-param name="pText" select="$vremText"/> <xsl:with-param name="pTargetStart" select="$pTargetStart"/> <xsl:with-param name="pTargetEnd" select="$pTargetEnd"/> <xsl:with-param name="pRep" select="$pRep"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet> 

when applied to the provided XML document :

 <xliff xmlns:xliff="urn:oasis:names:tc:xliff:document:1.1" version="1.1"> <file> <source>abc [[field1]] def [[field2]] ghi</source> </file> </xliff> 

creates the desired, correct result :

 <xliff xmlns:xliff="urn:oasis:names:tc:xliff:document:1.1" version="1.1"> <file> <source>abc F def F ghi</source> </file> </xliff> 

II. XSLT 2.0 solution (for comparison only):

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="source/text()"> <xsl:sequence select="replace(., '\[\[(.*?)\]\]', 'F')"/> </xsl:template> </xsl:stylesheet> 
+4
source

EXSLT has some nice features for you. If you need to replace simple strings, try str: replace . Introduced XSLT 1.0 implementation .

+2
source

EDIT 1

I just realized that the version of Dimitre uses recursion and is very similar; therefore my opening sentence seems silly now.

Here is the version using recursion:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:variable name="fld-beg" select="'[['"/> <xsl:variable name="fld-end" select="']]'"/> <xsl:variable name="replacement" select="'F'"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="source/text()"> <xsl:call-template name="replace"> <xsl:with-param name="str" select="."/> </xsl:call-template> </xsl:template> <xsl:template name="replace"> <xsl:param name="str"/> <xsl:choose> <xsl:when test="contains($str, $fld-beg) and contains($str, $fld-end)"> <xsl:call-template name="replace"> <xsl:with-param name="str" select="concat( substring-before($str, $fld-beg), $replacement, substring-after($str, $fld-end))"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$str"/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet> 

match="source/text()" matches all the text in the 'source' node as one line and passes it to the named replace template. 'replace' looks for occurrences at the beginning and end of the delimiters ('[[' and ']]'), and if the one found breaks the text into (and thus ignores) the delimiters, inserts a replacement string and passes it all to itself to repeat the process.

I say split, but given the lack of real split() in XPath 1.0, we can combine substring-before() and substring-after() .

Given the text in the source, 'abc [[field1]] def [[field2]] ghi' , the recursion looks like this: it is shown how it is broken, replaced and passed:

  • 'abc ' + 'F' + def [[field2]] ghi' , again moved to' replacement '
  • 'abc F def ' + 'F' + ' ghi' , again switched to 'replacement'
  • since there are no delimiters, 'abc F def F ghi' is passed back to match="source/text()"

Here's what it looks like with xsltproc :

 $ xsltproc so.xsl so.xml <?xml version="1.0"?> <xliff xmlns:xliff="urn:oasis:names:tc:xliff:document:1.1" version="1.1"> <file> <source>abc F def F ghi</source> </file> </xliff> 

Hope this helps.

+1
source

You can use Java inside XSL, for example for replaceAll:

 <xsl:template name="replace_all" xmlns:string="java.lang.String"> <xsl:param name="text"/> <xsl:param name="pattern"/> <xsl:param name="replace"/> <xsl:variable name="text_string" select="string:new($text)"/> <xsl:value-of select="string:replaceAll($text_string, $pattern, $replace)"/> </xsl:template> 

pattern is a regular expression. For more information see: javadoc String

+1
source

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


All Articles