randombits
randombits

Reputation: 48460

XSLT replace double quotes in variable

Just to clarify, I am using XSLT 1.0. Sorry for not specifying that at first.

I have an XSLT stylesheet where I'd like to replace double quotes with something safe that's safe to go into a JSON string. I'm trying to do something like the following:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" omit-xml-declaration="yes" />
  <xsl:strip-space elements="*" />

  <xsl:template match="/message">
    <xsl:variable name="body"><xsl:value-of select="body"/></xsl:variable>


    {
      "message" : 
      {
        "body":  "<xsl:value-of select="normalize-space($body)"/>"
      }
    }
  </xsl:template>
</xsl:stylesheet>

If I have XML passed in that looks like the following, this will always work just fine:

<message>
 <body>This is a normal string that will not give you any issues</body>
</message>

However, I'm dealing with a body that has full blown HTML in it, which isn't an issue because normalize-space() will take care of the HTML, but not double quotes. This breaks me:

<message>
<body>And so he quoted: "I will break him". The end.</body>
</message>

I really don't care if the double quotes are HTML escaped or prefixed with a backslash. I just need to make sure the end result passes a JSON parser.

This output passes JSON Lint and would be an appropriate solution (backslashing the quotes):

{ "body" : "And so he quoted: \"I will break him\". The end." } 

Upvotes: 1

Views: 10434

Answers (2)

harpo
harpo

Reputation: 43168

What version of XSLT? Keep in mind that many characters require special escaping in JSON. While this is technically possible in XSLT, it won't be pretty.

If you really just care about the backslash, though, and you're using XSLT 1.0, any of the various string replace templates should do it for you.

Upvotes: 2

Mads Hansen
Mads Hansen

Reputation: 66723

Using a recursive template, you can perform the replace. This example replaces " with \"

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" omit-xml-declaration="yes" />
    <xsl:strip-space elements="*" />

    <xsl:template match="/message">
        <xsl:variable name="escaped-body">
            <xsl:call-template name="replace-string">
                <xsl:with-param name="text" select="body"/>
                <xsl:with-param name="replace" select="'&quot;'" />
                <xsl:with-param name="with" select="'\&quot;'"/>
            </xsl:call-template>
        </xsl:variable>


        {
        "message" : 
        {
        "body":  "<xsl:value-of select="normalize-space($escaped-body)"/>"
        }
        }
    </xsl:template>

    <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>
</xsl:stylesheet>

And produces the output:

{
"message" : 
{
"body":  "And so he quoted: \"I will break him\". The end."
}
}

Upvotes: 8

Related Questions