(xslt 1.0) How to replace space with some string of all text values ​​in xml?

EDIT: [started with a character replacement and I ended up looking for a replacement string using Dimitre Novachev and Roland Buman

I think the code examples are sufficient to explain the requirements.

This is an example XML:

<root> <node1>text node</node1> <node2>space between the text</node2> <node3> has to be replaced with $</node3> </root> 

This is the result that I expect:

 <root> <node1>text$node</node1> <node2>space$between$the$text</node2> <node3>$has$to$be$replaced$with$$</node3> </root> 

I tried writing XSLT code that does not show the required output.
This is the code:

  <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="text()[.!='']"> <xsl:call-template name="rep_space"> <xsl:with-param name="text" select="."/> </xsl:call-template> </xsl:template> <xsl:template name="rep_space"> <xsl:param name="text"/> <xsl:variable name="temp" select="'&#x36;'"/> <xsl:choose> <xsl:when test="contains(text,'&#x32;')"> <xsl:call-template name="rep_space"> <xsl:with-param name="text" select="concat((concat(substring-before(text,' '),temp)),substring-after(text,' '))"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="text"/> </xsl:otherwise> </xsl:choose> </xsl:template> 

translate (., '', '$') works, but not to a satisfactory degree .. my questions are: .. what if it's a string instead of a character? I mean, suppose I intend to replace '' with "% 20"? And one more case. What if the input XML is not "Pretty Print XML", then all the space that appears in the XML is replaced with "$".

Enough to print XML is a file with the corresponding indentation (usually my input XML files never have this):

another node is @ low

You may notice that there are no whitespace in front of the <new> <test> nodes, but they are actually indented correctly (using Altova XMLSPY we can give a simple command in the editing menu. Any XML files for "pretty print XML") .

Where, as in the example below.

 <new> <test>one more node</test> <test2> <child>this is @ lower level</child> </test2> </new> 

There are spaces before all start tags. <child> tag has more spaces to <test2> node ..

With the second xml sample .. all whitespace characters are replaced with " %20 " .. so the output will be ...

 <new> %20%20<test>one%20more%20node</test> %20%20<test2> %20%20%20%20<child>this%20is% 20@ %20lower%20level</child> %20%20</test2> </new> 

Of course, this is not expected ..

Solutions sent by Dimitre Novatchev and Roland Bouman can also replace a string with another string, changing the parameters passed to the called template.

It was a great training @Dimitre, @Roland, I am very grateful and Thank you guys ..

Hi,
infant pro.

+3
source share
3 answers

As per Roland's wish, this is a tail recursive solution :

  <xsl:template name="replace"> <xsl:param name="ptext"/> <xsl:param name="ppattern"/> <xsl:param name="preplacement"/> <xsl:choose> <xsl:when test="not(contains($ptext, $ppattern))"> <xsl:value-of select="$ptext"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="substring-before($ptext, $ppattern)"/> <xsl:value-of select="$preplacement"/> <xsl:call-template name="replace"> <xsl:with-param name="ptext" select="substring-after($ptext, $ppattern)"/> <xsl:with-param name="ppattern" select="$ppattern"/> <xsl:with-param name="preplacement" select="$preplacement"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> 

Note that the recursive call is the last instruction in the template - this is what makes it tail recursive. The tail-recursive property allows an intelligent XSLT processor (such as Saxon or .NET XslCompiledTransform) to optimize the code, replacing recursion with a simple iteration.

Such code will not have an exception, even if the "nesting" of calls is equal to millions, while non-recursive (and recursive) code usually increases this stack overflow at a depth of about 1000 nested (this really depends on the amount of available memory).

What if the XSLT processor is not smart enough? Is there another way to avoid deep-level recursive calls that work with every XSLT processor?

Ask me a separate question, and I can tell you :)

+8
source

Check out the XPath translation function: http://www.w3.org/TR/xpath/#function-translate

 <xsl:template match="text()"> <xsl:value-of select="translate(., ' ', '$')"/> </xsl:template> 

If this is not a single character, but a string that you must replace, it takes much more effort, and you need a template to recursively replace the string:

 <xsl:template match="text()[not(../*)]"> <xsl:call-template name="replace"> <xsl:with-param name="text" select="."/> <xsl:with-param name="search" select="' '"/> <xsl:with-param name="replace" select="'%20'"/> </xsl:call-template> </xsl:template> <xsl:template name="replace"> <xsl:param name="text"/> <xsl:param name="search"/> <xsl:param name="replace"/> <xsl:choose> <xsl:when test="contains($text, $search)"> <xsl:variable name="replace-next"> <xsl:call-template name="replace"> <xsl:with-param name="text" select="substring-after($text, $search)"/> <xsl:with-param name="search" select="$search"/> <xsl:with-param name="replace" select="$replace"/> </xsl:call-template> </xsl:variable> <xsl:value-of select=" concat( substring-before($text, $search) , $replace , $replace-next ) " /> </xsl:when> <xsl:otherwise><xsl:value-of select="$text"/></xsl:otherwise> </xsl:choose> </xsl:template> 

Edit: changed match = "text ()" to match = "text () [not (../*)]", so the xml input should not be some kind of "pretty print XML" .. (to remove unwanted space replacements String "% 20" in such an XML file)

+4
source

The "printed xml file" solution is not really a solution.

Imagine you have a document like this:

 <a> <b> <c>OM G</c> <d>DI Y</d> </b> </a> 

Exit the currently accepted solution (after adding it to <xsl:stylesheet> and adding the identification rule:

 <a> %20<b> %20%20<c>O$M$G</c> %20%20<d>D$I$Y</d> %20</b> </a> 

Now, why does the proposed workaround not allow you to save the situation? As you can see from the above example, an element can have more than one child element with text nodes ...

What is the real solution ?

The creators of XSLT have thought about this issue. Using the right terminology, we want all minor text nodes with a space to be ignored by the XSLT processor, as if they were not part of the document tree at all. This is achieved using the <xsl:strip-space> command.

Just add this globally (as a child of <xsl:stylesheet> and, for readability, before any templates):

  <xsl:strip-space elements="*"/> 

and now you really have a working solution.

+1
source

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


All Articles