Srikanth Reddy Srikanth Reddy - 6 months ago 98
JSON Question

XSLT 1.0 escaping double quotes and backslash in a string

I have a string like below and trying to convert into json format:

Test "out" and \new

Expected output is
Test \"out\" and \new

I tried by calling templates for escapequote - working fine for escape quotes:

<xsl:template name="escapeQuote">
<xsl:param name="pText" select="concat(normalize-space(.), '')" />
<xsl:if test="string-length($pText) >0">
<xsl:value-of select="substring-before(concat($pText, '&quot;'), '&quot;')" />

<xsl:if test="contains($pText, '&quot;')">
<xsl:text>\"</xsl:text>
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText" select="substring-after($pText, '&quot;')" />
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>


Template for escape backslash - working for only backslash:

<xsl:template name="jsonescape">
<xsl:param name="str" select="."/>
<xsl:choose>
<xsl:when test="contains($str, '\')">
<xsl:value-of select="concat(substring-before($str, '\'), '\\' )"/>
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="substring-after($str, '\')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$str"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>


My question how to call both templates or merge, please help me

Answer

Here is an example of how you can combine the two template calls, so that the output from jsonescape is used as an input parameter to escapequote

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="text" />

  <xsl:template match="input">
    <xsl:call-template name="escapeQuote">
      <xsl:with-param name="pText">
        <xsl:call-template name="jsonescape">
          <xsl:with-param name="str" select="." />
        </xsl:call-template>          
      </xsl:with-param>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="escapeQuote">
      <xsl:param name="pText" select="concat(normalize-space(.), '')" />
      <xsl:if test="string-length($pText) >0">
          <xsl:value-of select="substring-before(concat($pText, '&quot;'), '&quot;')" />

          <xsl:if test="contains($pText, '&quot;')">
              <xsl:text>\"</xsl:text>    
              <xsl:call-template name="escapeQuote">
                  <xsl:with-param name="pText" select="substring-after($pText, '&quot;')" />
              </xsl:call-template>
          </xsl:if>
      </xsl:if>
  </xsl:template>

  <xsl:template name="jsonescape">
   <xsl:param name="str" select="."/>
    <xsl:choose>
      <xsl:when test="contains($str, '\')">
        <xsl:value-of select="concat(substring-before($str, '\'), '\\' )"/>
        <xsl:call-template name="jsonescape">
          <xsl:with-param name="str" select="substring-after($str, '\')"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
          <xsl:value-of select="$str"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

So, given this an input:

<input>Test "out" and \new</input>

The following is output

Test \"out\" and \\new

Note that the order is important, because if you reversed the order of the template calls, the " would get converted to \" by the escapequote template, which would then get converted to \\" by the jsonescape template.

Alternatively, as both template do a similar thing, of putting a \ before specific characters, you could combine the two templates into one.

Try this XSLT too

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="text" />

  <xsl:template match="input">
    <xsl:call-template name="jsonescape">
      <xsl:with-param name="str" select="." />
    </xsl:call-template>          
  </xsl:template>

  <xsl:template name="jsonescape">
   <xsl:param name="str" select="."/>
   <xsl:param name="escapeChars" select="'\&quot;'" />
   <xsl:variable name="first" select="substring(translate($str, translate($str, $escapeChars, ''), ''), 1, 1)" />
   <xsl:choose>
      <xsl:when test="$first">
        <xsl:value-of select="concat(substring-before($str, $first), '\', $first)"/>
        <xsl:call-template name="jsonescape">
          <xsl:with-param name="str" select="substring-after($str, $first)"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
          <xsl:value-of select="$str"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>