XSLT 1.0转义XML元素内双引号、单引号的问题求助
XSLT 1.0中转义XML元素内单双引号的问题解决
问题背景
需要将XML元素内的双引号(")转义为",单引号(')转义为',但遇到两个核心问题:
- 无法匹配文本中的单引号;
- 转义后的
"无法在输出中保持正确格式,要么被还原为",要么被过度转义为"。
当前XML输入
<?xml version="1.0" encoding="UTF-8"?> <foo> <bar> <att name="name">Let's fix these "errors".</att> </bar> </foo>
期望输出
<?xml version="1.0" encoding="UTF-8"?> <foo> <bar> <att name="name">Let's fix these "errors".</att> </bar> </foo>
尝试的XSLT代码(原代码)
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="att[@name ='name']"> <xsl:param name="content" select="text()" /> <xsl:variable name="replaceApos"> <xsl:call-template name="string-replace-all"> <xsl:with-param name="text" select="$content" /> <xsl:with-param name="replace" select="'&#39;'" /> <xsl:with-param name="by" select="'&apos;'" /> </xsl:call-template> </xsl:variable> <xsl:variable name="replaceQuot"> <xsl:call-template name="string-replace-all"> <xsl:with-param name="text" select="$replaceApos" /> <xsl:with-param name="replace" select="'&quot;'" /> <xsl:with-param name="by" select="'&amp;quot;'" /> </xsl:call-template> </xsl:variable> <att name="name"><xsl:value-of select="$replaceQuot"/></att> </xsl:template> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()" /> </xsl:copy> </xsl:template> <xsl:template name="string-replace-all"> <xsl:param name="text" /> <xsl:param name="replace" /> <xsl:param name="by" /> <xsl:choose> <xsl:when test="$text = '' or $replace = ''or not($replace)" > <!-- Prevent this routine from hanging --> <xsl:value-of select="$text" /> </xsl:when> <xsl:when test="contains($text, $replace)"> <xsl:value-of select="substring-before($text,$replace)" /> <xsl:value-of select="$by" /> <xsl:call-template name="string-replace-all"> <xsl:with-param name="text" select="substring-after($text,$replace)" /> <xsl:with-param name="replace" select="$replace" /> <xsl:with-param name="by" select="$by" /> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$text" /> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
问题解答
Q1:无法匹配单引号的解决办法
你之前的代码无法匹配单引号,是因为replace参数传入的是'字符串(而非单引号字符本身),自然无法匹配文本中的'。正确的做法是让replace参数的值为单引号字符本身:
- 若属性值用单引号包裹,内部单引号需转义为
',即<xsl:with-param name="replace" select="'''"/>; - 若属性值用双引号包裹,可直接写
<xsl:with-param name="replace" select='"'"'/>,两种写法都能得到单引号字符作为匹配目标。
Q2:双引号转义后格式异常的解决办法
XSLT默认会将输出中的&转义为&,导致你写入的&quot;最终输出为&amp;quot;,而直接写"会被还原为"。在XSLT 1.0中,可通过局部禁用输出转义解决,同时保证其他特殊字符正常转义:
核心调整点
- 传递替换值时,用带
disable-output-escaping="yes"的<xsl:text>包裹实体引用; - 修改
string-replace-all模板,用<xsl:copy-of>输出替换值,保留禁用转义的设置。
完整可用的XSLT代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="att[@name ='name']"> <xsl:param name="content" select="text()" /> <xsl:variable name="replaceApos"> <xsl:call-template name="string-replace-all"> <xsl:with-param name="text" select="$content" /> <xsl:with-param name="replace" select="'''" /> <xsl:with-param name="by"> <xsl:text disable-output-escaping="yes">&apos;</xsl:text> </xsl:with-param> </xsl:call-template> </xsl:variable> <xsl:variable name="replaceQuot"> <xsl:call-template name="string-replace-all"> <xsl:with-param name="text" select="$replaceApos" /> <xsl:with-param name="replace" select="'"'" /> <xsl:with-param name="by"> <xsl:text disable-output-escaping="yes">&quot;</xsl:text> </xsl:with-param> </xsl:call-template> </xsl:variable> <att name="name"><xsl:copy-of select="$replaceQuot"/></att> </xsl:template> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()" /> </xsl:copy> </xsl:template> <xsl:template name="string-replace-all"> <xsl:param name="text" /> <xsl:param name="replace" /> <xsl:param name="by" /> <xsl:choose> <xsl:when test="$text = '' or $replace = '' or not($replace)" > <xsl:value-of select="$text" /> </xsl:when> <xsl:when test="contains($text, $replace)"> <xsl:value-of select="substring-before($text,$replace)" /> <xsl:copy-of select="$by" /> <xsl:call-template name="string-replace-all"> <xsl:with-param name="text" select="substring-after($text,$replace)" /> <xsl:with-param name="replace" select="$replace" /> <xsl:with-param name="by" select="$by" /> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$text" /> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
补充说明
- 局部禁用输出转义仅针对替换的实体引用部分,不会影响文本中其他需要转义的字符(如
<、>),保证XML有效性; - 若上述方案仍无法满足需求,才考虑推动接收系统修复(正常XML解析应该能处理元素内的单双引号),或用其他工具做后续字符串处理。
内容的提问来源于stack exchange,提问作者rast
相关产品推荐
相关产品推荐

