如何在javascript中转义xml实体?


78

在JavaScript(服务器端nodejs)中,我正在编写一个生成xml作为输出的程序。

我通过连接字符串来构建xml:

str += '<' + key + '>';
str += value;
str += '</' + key + '>';

问题是:如果value包含'&''>'或字符,该'<'怎么办?逃脱这些角色的最佳方法是什么?

还是周围有可以逃避XML实体的JavaScript库?

Answers:


118

HTML编码简单地更换&"'<>与他们的实体当量字符。顺序很重要,如果您不先替换&字符,则会对某些实体进行双重编码:

if (!String.prototype.encodeHTML) {
  String.prototype.encodeHTML = function () {
    return this.replace(/&/g, '&amp;')
               .replace(/</g, '&lt;')
               .replace(/>/g, '&gt;')
               .replace(/"/g, '&quot;')
               .replace(/'/g, '&apos;');
  };
}

正如@Johan BW de Vries指出的那样,这将与标签名称有关,我想澄清一下,我假设这用于value

相反,如果要解码HTML实体1,请确保在其他所有内容之后都解码&amp;&,以免对任何实体进行双重解码:

if (!String.prototype.decodeHTML) {
  String.prototype.decodeHTML = function () {
    return this.replace(/&apos;/g, "'")
               .replace(/&quot;/g, '"')
               .replace(/&gt;/g, '>')
               .replace(/&lt;/g, '<')
               .replace(/&amp;/g, '&');
  };
}

1只是基础知识,不包括&copy;to©或其他此类事物


就图书馆而言。Underscore.js(或Lodash如果你愿意)提供了一个_.escape执行此功能的方法。


2
这几乎涵盖了5个XML实体。只需要@apos;
瑞安

2
看起来好像一次又一次地替换相同的字符串,这在处理大量数据时可能会提高性能。有更快的选择吗?
Jonny 2014年

2
@Jonny,与多次调用相比,正则表达式将提供更差的性能.replace()。无论哪种情况,您都必须拥有大量数据才能注意到任何重大问题。一种更快的替代方法是对您的应用进行基准测试并找到实际的阻塞点(通常是嵌套循环),而不用担心像这样微不足道的事情。
zzzzBov 2014年

1
我在Google Spreadsheet中有100-200行数据。我将其转换为plists(xml),不得不替换那些xml实体。我使用上面的代码编写了一个自定义javascript函数。它有效,但是非常慢。电子表格有时会令人窒息,但因为它只是“一次执行”的步骤,所以最终的速度并不重要。
强尼

4
我知道这个答案很旧,但是只是为了向JS的新手说明:将随机函数(对于某些标准化建议不是polyfill)附加到全局原型是一个坏主意。
austin_ce

103

同样的结果可能会更有效:

function escapeXml(unsafe) {
    return unsafe.replace(/[<>&'"]/g, function (c) {
        switch (c) {
            case '<': return '&lt;';
            case '>': return '&gt;';
            case '&': return '&amp;';
            case '\'': return '&apos;';
            case '"': return '&quot;';
        }
    });
}

这似乎是一个更好的解决方案。为什么不打tic?
维克多·格拉齐

1
@VictorGrazi:是的,它在50的49中测试了更快的解决方案。可能是因为它比接受的答案年轻了将近5年。
塞巴斯蒂安

1
@Sebastian啊,谢谢您的解释。在这里看乡亲^ ^ ^ ^这是您想要的解决方案!!!
维克多·格拉齐

与接受的答案相比,这是给我更好的解决方案,答案是遍历整个字符串五次(从长远来看,减小了JS引擎优化的范围),以寻找与单个字符匹配的内容。hgoebl的解决方案只遍历输入字符串一次,尝试将每个字符匹配到五个条件之一。问题是什么更昂贵:1)遍历字符串;或:2)将每个字符与5个可能的字符进行匹配。我的直觉是1)会更昂贵。
Jamie Birch

1
如果输入是HTML,则@RanLottem解码要复杂得多,请参阅Wikipedia。最好使用解析器(XML或文档)。
hgoebl

21

如果您使用jQuery,这是一个简单的解决方案:

  String.prototype.htmlEscape = function() {
    return $('<div/>').text(this.toString()).html();
  };

像这样使用它:

"<foo&bar>".htmlEscape(); -> "&lt;foo&amp;bar&gt"


1
我喜欢这种技术,因为它具有“让浏览器执行此操作”的态度。除了性能较差之外,还有其他缺点,因为这是通过DOM API进行的?
avernet 2012年

单引号和双引号不能通过这种技术进行转义:$('<div/>').text('<&\'>"').html() -> "&lt;&amp;'&gt;""
majgis

1
单引号和双引号通常不需要转义。
lambshaanxy 2015年

7

您可以使用以下方法。我已经在原型中添加了它,以便于访问。如果调用两次或两次以上,我也使用负前瞻,因此它不会弄乱事情。

用法:

 var original = "Hi&there";
 var escaped = original.EncodeXMLEscapeChars();  //Hi&amp;there

解码是在XML解析器中自动进行的。

方法 :

//String Extenstion to format string for xml content.
//Replces xml escape chracters to their equivalent html notation.
String.prototype.EncodeXMLEscapeChars = function () {
    var OutPut = this;
    if ($.trim(OutPut) != "") {
        OutPut = OutPut.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
        OutPut = OutPut.replace(/&(?!(amp;)|(lt;)|(gt;)|(quot;)|(#39;)|(apos;))/g, "&amp;");
        OutPut = OutPut.replace(/([^\\])((\\\\)*)\\(?![\\/{])/g, "$1\\\\$2");  //replaces odd backslash(\\) with even.
    }
    else {
        OutPut = "";
    }
    return OutPut;
};

3
未被重视的优秀解决方案。确保您不会被臭名昭著的&amp; amp; 输出中的字符串很漂亮。
史蒂夫·威斯布鲁克

使用此代码,您只需在所有应用程序中编辑String的所有实例,例如,let a = 'foo'将受到此代码的影响。更好地创建辅助函数,而不是扩展原型。
卢卡斯·里西斯

请不要突变内置对象,因为它会导致冲突,因此这是非常差的做法。
slikts

1

我最初在生产代码中使用了可接受的答案,但发现大量使用它实际上确实很慢。这里是一个很大更快的解决方案(运行超过两倍的速度):

   var escapeXml = (function() {
        var doc = document.implementation.createDocument("", "", null)
        var el = doc.createElement("temp");
        el.textContent = "temp";
        el = el.firstChild;
        var ser =  new XMLSerializer();
        return function(text) {
            el.nodeValue = text;
            return ser.serializeToString(el);
        };
    })();

console.log(escapeXml("<>&")); //&lt;&gt;&amp;

这假定您有document对象。我没有
卢卡斯·里西斯

1

也许你可以尝试一下

function encodeXML(s) {
  const dom = document.createElement('div')
  dom.textContent = s
  return dom.innerHTML
}

参考


1

注意,如果在XML中包含XML,则所有的正则表达式都不好。
而是将字符串循环一次,并替换所有转义字符。
这样一来,您就无法两次超过同一个角色。

function _xmlAttributeEscape(inputString)
{
    var output = [];

    for (var i = 0; i < inputString.length; ++i)
    {
        switch (inputString[i])
        {
            case '&':
                output.push("&amp;");
                break;
            case '"':
                output.push("&quot;");
                break;
            case "<":
                output.push("&lt;");
                break;
            case ">":
                output.push("&gt;");
                break;
            default:
                output.push(inputString[i]);
        }


    }

    return output.join("");
}


0

如果以前有东西逃脱,您可以尝试一下,因为这不会像其他许多东西一样使逃脱

function escape(text) {
    return String(text).replace(/(['"<>&'])(\w+;)?/g, (match, char, escaped) => {
        if(escaped) 
            return match

        switch(char) {
            case '\'': return '&quot;'
            case '"': return '&apos;'
            case '<': return '&lt;'
            case '>': return '&gt;'
            case '&': return '&amp;'
        }
    })
}

-1

这很简单:

sText = ("" + sText).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;");

1
与上面的替换方法相比,该“简单”方法在哪个世界?
山姆·霍尔德

简单易写,我没有说它比上面的简单。只是有所不同
Per Ghosh

3
我为尝试考虑一个更糟糕的解决方案而感到困惑
developerbmw

@developerbmw如果您不想添加方法并且不使用jquery,这是最好的解决方案之一
Per Ghosh

1
@developerbmw出于可读性考虑,有时最好以一种方式编写代码,以实现从上至下的读取功能。动态语言非常容易使人们无法快速阅读。这取决于实际情况。还使用在不同场景中使用并需要其自己的逻辑的组件。如果仅在一种方法中使用逻辑,则较小的组件可能不需要功能。我是C ++开发人员,已经编写了很多C语言。C语言非常易于阅读,如果您知道为什么,那么您知道我对此的论点
Per Ghosh
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.