我有一个HTML文档,在某些属性中可能有<
和>
。我试图提取这个并通过XSLT运行它,但XSLT引擎错误地告诉我<
在属性内无效。
我做了一些挖掘,发现它在源文档中被正确地转义了,但是当通过innerHTML
将其加载到DOM中时,DOM是对属性进行解码。奇怪的是,它对<
和>
这样做,但对&
等其他一些不这样做。
下面是一个简单的例子:
var div = document.createElement('DIV');
div.innerHTML = '<div asdf="<50" fdsa="&50"></div>';
console.log(div.innerHTML)
我假设DOM实现决定HTML属性可以比XML属性更不严格,并且这是"按预期工作"。我的问题是,我能在不写一些可怕的正则表达式替换的情况下解决这个问题吗?
尝试XMLSerializer:
var div = document.getElementById('d1');
var pre = document.createElement('pre');
pre.textContent = div.outerHTML;
document.body.appendChild(pre);
pre = document.createElement('pre');
pre.textContent = new XMLSerializer().serializeToString(div);
document.body.appendChild(pre);
<div id="d1" data-foo="a < b && b > c">This is a test</div>
您可能需要调整XSLT以考虑XHTML名称空间XMLSerializer插入(至少在Firefox测试中是这样)。
我不确定这是否是你正在寻找的,但请看看。
var div1 = document.createElement('DIV');
var div2 = document.createElement('DIV');
div1.setAttribute('asdf','<50');
div1.setAttribute('fdsa','&50');
div2.appendChild(div1);
console.log(div2.innerHTML.replace(/&/g, '&'));
对我来说最有效的方法是在传入的文档上使用XSLT对它们进行双转义(并在传出的文档上反向转义)。
所以属性中的<
变成了&lt;
。感谢@Abel的建议。
下面是我添加的XSLT,以防其他人觉得它有帮助:
首先是用于在XSLT 1.0中进行字符串替换的模板。如果可以使用XSLT 2.0,则可以使用内置的replace
。
<xsl:template name="string-replace-all">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="by"/>
<xsl:choose>
<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>
接下来是我需要的特定替换的模板:
<!-- xml -> html -->
<xsl:template name="replace-html-codes">
<xsl:param name="text"/>
<xsl:variable name="lt">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="replace" select="'<'"/>
<xsl:with-param name="by" select="'&lt;'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="gt">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$lt"/>
<xsl:with-param name="replace" select="'>'"/>
<xsl:with-param name="by" select="'&gt;'"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$gt"/>
</xsl:template>
<!-- html -> xml -->
<xsl:template name="restore-html-codes">
<xsl:param name="text"/>
<xsl:variable name="lt">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="replace" select="'&lt;'"/>
<xsl:with-param name="by" select="'<'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="gt">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$lt"/>
<xsl:with-param name="replace" select="'&gt;'"/>
<xsl:with-param name="by" select="'>'"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$gt"/>
</xsl:template>
XSLT主要是传递。我只是在复制属性时调用适当的模板:
<xsl:template match="@*">
<xsl:attribute name="data-{local-name()}">
<xsl:call-template name="replace-html-codes">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:attribute>
</xsl:template>
<!-- copy all nodes -->
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
值得提及的几件事可能对某人有所帮助:
- 确保你的HTML是真正有效的,例如,我不小心使用
时,我应该有
/
,它导致了这个问题。 - 正如OP在问题中指出的,您可以使用
&
,因此您可以尝试例如&lt;
和&gt;
。 -
<
和>
的替代品看起来很相似。 -
<
和>
有另一种表达方式:<
和>
。