在跨浏览器方式中使用Javascript的DOMParser时,如何检测XML解析错误?

csp*_*ode 24 javascript xml

似乎所有主流浏览器都实现DOMParser API,以便可以将XML解析为DOM,然后使用XPath,getElementsByTagName等查询...

但是,检测解析错误似乎更棘手. DOMParser.prototype.parseFromString始终返回有效的DOM.当发生解析错误时,返回的DOM包含一个<parsererror>元素,但在每个主要浏览器中它略有不同.

示例JavaScript:

xmlText = '<root xmlns="http://default" xmlns:other="http://other"><child><otherr:grandchild/></child></root>';
parser = new DOMParser();
dom = parser.parseFromString(xmlText, 'text/xml');
console.log((new XMLSerializer()).serializeToString(dom));
Run Code Online (Sandbox Code Playgroud)

Opera中的结果:

DOM的根是一个<parsererror>元素.

<?xml version="1.0"?><parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">Error<sourcetext>Unknown source</sourcetext></parsererror>
Run Code Online (Sandbox Code Playgroud)

Firefox中的结果:

DOM的根是一个<parsererror>元素.

<?xml-stylesheet href="chrome://global/locale/intl.css" type="text/css"?>
<parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">XML Parsing Error: prefix not bound to a namespace
Location: http://fiddle.jshell.net/_display/
Line Number 1, Column 64:<sourcetext>&lt;root xmlns="http://default" xmlns:other="http://other"&gt;&lt;child&gt;&lt;otherr:grandchild/&gt;&lt;/child&gt;&lt;/root&gt;
---------------------------------------------------------------^</sourcetext></parsererror>
Run Code Online (Sandbox Code Playgroud)

Safari中的结果:

<root>元素正确分析,但包含嵌套<parsererror>在不同的命名空间比Opera和Firefox的<parsererror>元素.

<root xmlns="http://default" xmlns:other="http://other"><parsererror xmlns="http://www.w3.org/1999/xhtml" style="display: block; white-space: pre; border: 2px solid #c77; padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black"><h3>This page contains the following errors:</h3><div style="font-family:monospace;font-size:12px">error on line 1 at column 50: Namespace prefix otherr on grandchild is not defined
</div><h3>Below is a rendering of the page up to the first error.</h3></parsererror><child><otherr:grandchild/></child></root>
Run Code Online (Sandbox Code Playgroud)

我是否缺少一种简单的跨浏览器方式来检测XML文档中是否出现解析错误?或者我必须查询<parsererror>不同浏览器可能生成的每个可能元素的DOM 吗?

csp*_*ode 18

这是我提出的最佳解决方案.

我尝试解析故意无效的XML字符串并观察结果<parsererror>元素的命名空间.然后,在解析实际的XML时,我可以getElementsByTagNameNS用来检测相同类型的<parsererror>元素并抛出一个Javascript Error.

// My function that parses a string into an XML DOM, throwing an Error if XML parsing fails
function parseXml(xmlString) {
    var parser = new DOMParser();
    // attempt to parse the passed-in xml
    var dom = parser.parseFromString(xmlString, 'text/xml');
    if(isParseError(dom)) {
        throw new Error('Error parsing XML');
    }
    return dom;
}

function isParseError(parsedDocument) {
    // parser and parsererrorNS could be cached on startup for efficiency
    var parser = new DOMParser(),
        errorneousParse = parser.parseFromString('<', 'text/xml'),
        parsererrorNS = errorneousParse.getElementsByTagName("parsererror")[0].namespaceURI;

    if (parsererrorNS === 'http://www.w3.org/1999/xhtml') {
        // In PhantomJS the parseerror element doesn't seem to have a special namespace, so we are just guessing here :(
        return parsedDocument.getElementsByTagName("parsererror").length > 0;
    }

    return parsedDocument.getElementsByTagNameNS(parsererrorNS, 'parsererror').length > 0;
};
Run Code Online (Sandbox Code Playgroud)

请注意,此解决方案不包括Internet Explorer所需的特殊外壳.但是,IE中的事情要简单得多.使用一种loadXML方法解析XML ,如果解析成功或失败,则分别返回true或false.有关示例,请参见http://www.w3schools.com/xml/xml_parser.asp.

  • 我通过以下函数和 Rast 函数得到以下信息: TypeError: Cannot read property 'namespaceURI' of undefined (2认同)

Ras*_*ast 13

当我第一次来到这里时,我赞​​成原始答案(通过cspotcode),然而,它在Firefox中不起作用.由于生成的文档的结构,生成的命名空间始终为"null".我做了一些研究(在这里查看代码).这个想法是不使用

invalidXml.childNodes[0].namespaceURI
Run Code Online (Sandbox Code Playgroud)

invalidXml.getElementsByTagName("parsererror")[0].namespaceURI
Run Code Online (Sandbox Code Playgroud)

然后在原始答案中按名称空间选择"parsererror"元素.但是,如果您有一个有效的XML文档,其<parsererror>标记位于浏览器使用的相同名称空间中,则最终会出现误报.所以,这是一个启发式方法来检查你的XML是否成功解析:

function tryParseXML(xmlString) {
    var parser = new DOMParser();
    var parsererrorNS = parser.parseFromString('INVALID', 'text/xml').getElementsByTagName("parsererror")[0].namespaceURI;
    var dom = parser.parseFromString(xmlString, 'text/xml');
    if(dom.getElementsByTagNameNS(parsererrorNS, 'parsererror').length > 0) {
        throw new Error('Error parsing XML');
    }
    return dom;
}
Run Code Online (Sandbox Code Playgroud)

为什么不在DOMParser中实现异常?

有趣的事情在当前的上下文中值得一提:如果你试图获取XML文件XMLHttpRequest,解析的DOM将存储在responseXML属性中,或者null,如果XML文件内容无效.不是例外,不是parsererror或另一个具体指标.只是null.


Tho*_*kow 8

2022 年回到这个问题,该方法的文档DOMParser.parseFromString()提供了一个更简单的解决方案:

const parser = new DOMParser();

const xmlString = "<warning>Beware of the missing closing tag";
const doc = parser.parseFromString(xmlString, "application/xml");
const errorNode = doc.querySelector('parsererror');
if (errorNode) {
  // parsing failed
} else {
  // parsing succeeded
}
Run Code Online (Sandbox Code Playgroud)

虽然接受的答案对我有用,但使用该Document.querySelector()方法确实要简单得多,因为您不必确定namespaceURI元素的parsererror