3

I am using XSLT to transform a XML into a html/php file. In this XSLT I replace some tags by php code and now I have to pass attribute values into that php code. My problem now is that I have to escape single quotes with a backslash to get it work. Is this possible with XSLT.

Example:

<xsl:template match="foo">
    <xsl:processing-instruction name="php">$this->doSomething('<xsl:value-of select="./@bar" />');</xsl:processing-instruction>
</xsl:template>

If I now had a template:

<foo bar="test'xyz"/>

This would generate:

<?php $this->doSomething('test'xyz');?>

What I now want to achieve is the following:

<?php $this->doSomething('test\'xyz');?>

So I want to replace all single quotes by \'

stofl
  • 2,950
  • 6
  • 35
  • 48

5 Answers5

7

Use a recursive template to do the find/replace:

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

Applied to your example:

   <xsl:template match="foo">
    <xsl:processing-instruction name="php">
        <xsl:text>$this->doSomething('</xsl:text>
        <xsl:call-template name="replace-string">
            <xsl:with-param name="text" select="./@bar"/>
            <xsl:with-param name="replace" select='"&apos;"' />
            <xsl:with-param name="with" select='"\&apos;"'/>
        </xsl:call-template>
        <xsl:text>');</xsl:text>
    </xsl:processing-instruction>
</xsl:template>

Note:

  1. The use of <xsl:text> to explicitly define text intended for the output, and not have to worry about whitespace between that text and template calls.
  2. The use of single quotes to enclose the select statement for the replace and with parameters, in order to use double-quotes to indicate a text statement that contains a single quote
  3. The use of the entity reference &apos; for the single quote (a.k.a. apostrophe)
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
3

Here's a simpler, inelegant, but quick method for replacing single quotes:

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

1) Define a variable that contains just an apostrophe. xsl:text is required to get xsl to treat ' as a simple character

2) Use replace function using that variable as the string to match. In this example, I'm simply removing it.

AndrewC
  • 31
  • 2
2

Why don't you just use the standard XSLT 2.0 replace function? Or the XSLT 1.0 replace implementation xslt 1.0 string replace function

Community
  • 1
  • 1
FailedDev
  • 26,680
  • 9
  • 53
  • 73
  • 1
    Hm.. @_FailedDev, The link you provide is for replacing single characters with single characters. This cannot be used in the current problem, where it is necessary to replace one character (') with two characters (\'). – Dimitre Novatchev Oct 10 '11 at 12:38
  • @DimitreNovatchev somehow magically the link copied from the available answer we wrong. Thanks for pointing this out. – FailedDev Oct 10 '11 at 12:43
2

For an XSLT 1.0 solution either write your own recursive solution, or you can use the FXSL template str-map:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:testmap="testmap" xmlns:f="http://fxsl.sf.net/"
exclude-result-prefixes="xsl f testmap"
>
   <xsl:import href="str-dvc-map.xsl"/>

   <testmap:testmap/>

   <xsl:variable name="vTarget">'</xsl:variable>
   <xsl:variable name="vReplacement">\</xsl:variable>

   <xsl:output omit-xml-declaration="yes" indent="yes"/>

   <xsl:template match="/">
     <xsl:variable name="vTestMap" select="document('')/*/testmap:*[1]"/>
     <xsl:call-template name="str-map">
       <xsl:with-param name="pFun" select="$vTestMap"/>
       <xsl:with-param name="pStr" select="string(/*/@bar)"/>
     </xsl:call-template>
   </xsl:template>

    <xsl:template name="escapeApos" mode="f:FXSL"
         match="*[namespace-uri() = 'testmap']">
      <xsl:param name="arg1"/>

      <xsl:if test="$arg1 = $vTarget">
       <xsl:value-of select="$vReplacement"/>
      </xsl:if>
      <xsl:value-of select="$arg1"/>
    </xsl:template>
</xsl:stylesheet>

When applied on the provided XML document:

<foo bar="test'xyz"/>

the wanted, correct result is produced:

test\'xyz
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Thank you again. But I don't want to use FXSL in the current project. – stofl Oct 10 '11 at 17:03
  • @stofl: FXSL offers a set of very powerful, generic and reusable functions -- which for example in many cases (as this one) totally eliminate the need for the developer to code their own recursive template. Taking into account that a developer typically writes "almost the same" recursive template all over again and again, this results in considerable time savings. Also. one thinks at a higher level and is able to accomplish more in the same time. Last, but not least, any possibility for commiting an error in the manual writing of recursive code is completely eliminated. :) – Dimitre Novatchev Oct 10 '11 at 23:17
-1

Just use:

$string = str_replace( "'", "\\'", $string );
Tobias
  • 9,170
  • 3
  • 24
  • 30