在使用 XMLSerializer() 序列化 XML 之前从 XML 中删除无效字符

Removing invalid characters from XML before serializing it with XMLSerializer()

本文关键字:XML 删除 字符 无效 XMLSerializer 序列化      更新时间:2023-09-26

我正在尝试将用户输入存储在客户端(javascript)的XML文档中,并将其传输到服务器以进行持久性。

例如,一个用户粘贴的文本包含 STX 字符 (0x2)。XMLSerializer 未转义 STX 字符,因此未序列化为格式正确的 XML。或者,也许 .attr() 调用应该转义了 STX 字符,但在这两种情况下,都会生成无效的 XML。

我发现浏览器内XMLSerializer()的输出并不总是格式良好的(甚至不满足浏览器自己的DOMParser()

此示例显示 XMLSerializer() 未正确编码 STX 字符:

> doc = $.parseXML('<?xml version="1.0" encoding="utf-8" ?>'n<elem></elem>');
    #document
> $(doc).find("elem").attr("someattr", String.fromCharCode(0x2));
    [ <elem someattr=​"">​</elem>​ ]
> serializedDoc = new XMLSerializer().serializeToString(doc);
    "<?xml version="1.0" encoding="utf-8"?><elem someattr=""/></elem>"
> $.parseXML(serializedDoc);
    Error: Invalid XML: <?xml version="1.0" encoding="utf-8"?><elem someattr=""/></elem>

我应该如何在浏览器中构造一个XML文档(参数由任意用户输入确定),以便它始终格式正确(所有内容都正确转义)?我不需要支持 IE8 或 IE7。

(是的,我确实在服务器端验证了XML,但是如果浏览器向服务器提供了格式不正确的文档,服务器所能做的最好的事情就是拒绝它,这对可怜的用户没有多大帮助)

这是一个函数sanitizeStringForXML(),它可以用来在赋值前清理字符串,或者一个派生函数removeInvalidCharacters(xmlNode),它可以传递一个DOM树,并将自动清理属性和textNode,以便它们安全存储。

var stringWithSTX = "Bad" + String.fromCharCode(2) + "News";
var xmlNode = $("<myelem/>").attr("badattr", stringWithSTX);
var serializer = new XMLSerializer();
var invalidXML = serializer.serializeToString(xmlNode);
// Now cleanse it:
removeInvalidCharacters(xmlNode);
var validXML = serializer.serializeToString(xmlNode);

我基于这篇维基百科文章的非限制字符部分中的字符列表,但补充平面需要 5 个十六进制数字的 unicode 字符,而 Javascript 正则表达式不包括语法,所以现在,我只是将它们删除(你没有错过太多......

// WARNING: too painful to include supplementary planes, these characters (0x10000 and higher) 
// will be stripped by this function. See what you are missing (heiroglyphics, emoji, etc) at:
// http://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Multilingual_Plane
var NOT_SAFE_IN_XML_1_0 = /[^'x09'x0A'x0D'x20-'xFF'x85'xA0-'uD7FF'uE000-'uFDCF'uFDE0-'uFFFD]/gm;
function sanitizeStringForXML(theString) {
    "use strict";
    return theString.replace(NOT_SAFE_IN_XML_1_0, '');
}
function removeInvalidCharacters(node) {
    "use strict";
    if (node.attributes) {
        for (var i = 0; i < node.attributes.length; i++) {
            var attribute = node.attributes[i];
            if (attribute.nodeValue) {
                attribute.nodeValue = sanitizeStringForXML(attribute.nodeValue);
            }
        }
    }
    if (node.childNodes) {
        for (var i = 0; i < node.childNodes.length; i++) {
            var childNode = node.childNodes[i];
            if (childNode.nodeType == 1 /* ELEMENT_NODE */) {
                removeInvalidCharacters(childNode);
            } else if (childNode.nodeType == 3 /* TEXT_NODE */) {
                if (childNode.nodeValue) {
                    childNode.nodeValue = sanitizeStringForXML(childNode.nodeValue);
                }
            }
        }
    }
}

请注意,这只会从属性和文本节点的节点值中删除无效字符。它不检查标签名称或属性名称、注释等。

检查https://gist.github.com/john-doherty/b9195065884cdbfd2017a4756e6409cc,

非常有用的要点,示例用法:

const resultXml = removeXMLInvalidChars(INPUT_XML_STRING, true);