I am defining a JavaScript variable from XSLT and am getting an error due to an unescaped string. I understand that I need to replace this to '
, but I'm unsure how to do that in XSLT 1.0.
XSLT example:
var currentComment = '<xsl:value-of select="root/Reviews/Review/Comment" />';
Rendered javascript with unescaped single quote:
var currentComment = 'Let's test a string.',
// Causing an error ------^
As Ian Roberts pointed out in his comment, you need to account for backslashes and not just apostrophes. Dimitre's answer can be modified as follows to account for this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:text>var currentComment = '</xsl:text>
<xsl:apply-templates select="root/value" mode="escape" />
<xsl:text>'
</xsl:text>
<!-- Example with placing the escaped value in a variable first -->
<xsl:variable name="escapedOther">
<xsl:apply-templates select="root/otherValue" mode="escape" />
</xsl:variable>
<xsl:value-of select='concat("var otherComment = '", $escapedOther, "'")' />
</xsl:template>
<xsl:template match="@* | node()" mode="escape">
<!-- Escape the apostrophes second -->
<xsl:call-template name="replace">
<xsl:with-param name="pTarget" select='"'"' />
<xsl:with-param name="pReplacement" select='"\'"'/>
<xsl:with-param name="pText">
<!-- Escape the backslashes first, and then pass that result directly into the next template -->
<xsl:call-template name="replace">
<xsl:with-param name="pTarget" select="'\'" />
<xsl:with-param name="pReplacement" select="'\\'" />
<xsl:with-param name="pText" select="." />
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="replace">
<xsl:param name="pText"/>
<xsl:param name="pTarget" select='"'"'/>
<xsl:param name="pReplacement" select="'\"'"/>
<xsl:if test="$pText">
<xsl:value-of select='substring-before(concat($pText,$pTarget),$pTarget)'/>
<xsl:if test='contains($pText, $pTarget)'>
<xsl:value-of select='$pReplacement'/>
</xsl:if>
<xsl:call-template name="replace">
<xsl:with-param name="pText" select='substring-after($pText, $pTarget)'/>
<xsl:with-param name="pTarget" select="$pTarget"/>
<xsl:with-param name="pReplacement" select="$pReplacement"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Any time you need to escape something, you can just use apply-templates
on it with mode escape
. For this input XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<value>Let's test a string that refers to something like the C:\ drive's \Program Files Directory.</value>
<otherValue>This value has a bunch of apostrophes '''' and backslashes \\\\ in it</otherValue>
</root>
This produces:
var currentComment = 'Let\'s test a string that refers to something like the C:\\ drive\'s \\Program Files Directory.'
var otherComment = 'This value has a bunch of apostrophes \'\'\'\' and backslashes \\\\\\\\'
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vSomething">Let's test a string.</xsl:variable>
<xsl:template match="/">
<xsl:text>var currentComment = '</xsl:text>
<xsl:call-template name="replace">
<xsl:with-param name="pText" select="$vSomething"/>
</xsl:call-template>'
</xsl:template>
<xsl:template name="replace">
<xsl:param name="pText"/>
<xsl:param name="pTarget" select='"'"'/>
<xsl:param name="pReplacement" select='"\'"'/>
<xsl:if test="$pText">
<xsl:value-of select='substring-before(concat($pText,$pTarget),$pTarget)'/>
<xsl:if test='contains($pText, $pTarget)'>
<xsl:value-of select="$pReplacement"/>
</xsl:if>
<xsl:call-template name="replace">
<xsl:with-param name="pText" select='substring-after($pText, $pTarget)'/>
<xsl:with-param name="pTarget" select="$pTarget"/>
<xsl:with-param name="pReplacement" select="$pReplacement"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on any XML document (not used), produces the wanted, correct result:
var currentComment = 'Let\'s test a string.'
This is just an implementation of the JLRische-Novachev solution I did to accomplish my xml.
Maybe it can be usefull:
<?xml version="1.0" encoding="utf-8"?>
<root>
<value msg="1- Let's test a string that refers to something like the C:\ drive's \Program Files Directory." num="1"/>
<value msg="2- Let's test a string that refers to something like the C:\ drive's \Program Files Directory." num="2"/>
</root>
xsl:
...
<xsl:template match="@* | node()" mode="escape">
<!-- Escape the apostrophes second -->
<xsl:call-template name="replace">
<xsl:with-param name="pTarget" select='"'"' />
<xsl:with-param name="pReplacement" select='"\'"'/>
<xsl:with-param name="pText">
<!-- Escape the backslashes first, and then pass that result directly into the next template -->
<xsl:call-template name="replace">
<xsl:with-param name="pTarget" select="'\'" />
<xsl:with-param name="pReplacement" select="'\\'" />
<xsl:with-param name="pText" select="./@msg" />
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
<xsl:text>, Number: </xsl:text><xsl:value-of select="./@num"/><xsl:text>
</xsl:text>
...
Output:
1- Let\'s test a string that refers to something like the C:\\ drive\'s \\Program Files Directory., Number: 1
2- Let\'s test a string that refers to something like the C:\\ drive\'s \\Program Files Directory., Number: 2