30

すべての主要なブラウザーはDOMParserAPIを実装しているため、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, 'application/xml');
console.log((new XMLSerializer()).serializeToString(dom));

Operaでの結果:

DOMのルートは<parsererror>要素です。

<?xml version="1.0"?><parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">Error<sourcetext>Unknown source</sourcetext></parsererror>

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>

Safariでの結果:

<root>要素は正しく解析されますが、OperaおよびFirefoxの要素とは異なる名前空間にネストされて<parsererror>います。<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>

XMLドキュメントのどこかで解析エラーが発生したかどうかを検出する簡単なクロスブラウザーの方法がありませんか?<parsererror>または、さまざまなブラウザが生成する可能性のある要素ごとにDOMにクエリを実行する必要がありますか?

4

4 に答える 4

23

これは私が思いついた最良の解決策です。

<parsererror>意図的に無効なXMLである文字列を解析し、結果の要素の名前空間を観察しようとしました。次に、実際の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, 'application/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('<', 'application/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;
};

このソリューションには、InternetExplorerに必要な特殊なケーシングが含まれていないことに注意してください。ただし、IEの方がはるかに簡単です。XMLはloadXML、解析が成功または失敗した場合にそれぞれtrueまたはfalseを返すメソッドを使用して解析されます。例については、 http://www.w3schools.com/xml/xml_parser.aspを参照してください。

于 2012-07-24T02:19:13.597 に答える
17

初めてここに来たとき、元の回答(cspotcodeによる)に賛成しましたが、Firefoxでは機能しません。生成されたドキュメントの構造により、結果の名前空間は常に「null」になります。私は少し調査しました(ここのコードをチェックしてください)。アイデアは使用しないことです

invalidXml.childNodes[0].namespaceURI

しかし

invalidXml.getElementsByTagName("parsererror")[0].namespaceURI

次に、元の回答と同様に、名前空間ごとに「parsererror」要素を選択します。<parsererror>ただし、ブラウザで使用されているのと同じ名前空間にタグが付いた有効なXMLドキュメントがある場合は、誤ったアラームが発生します。したがって、XMLが正常に解析されたかどうかを確認するヒューリスティックは次のとおりです。

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

DOMParserに例外を実装してみませんか?

現在のコンテキストで言及する価値のある興味深いこと:でXMLファイルを取得しようとすると、解析されたDOMがプロパティにXMLHttpRequest格納されます。XMLファイルの内容が無効である場合は。例外ではなく、そうでないか、別の特定の指標です。ただヌル。responseXMLnullparsererror

于 2013-11-29T22:34:12.873 に答える
1

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
}

受け入れられた答えは私にとってはうまくいきましたが、要素Document.querySelector()のを決定する必要がないため、メソッドの使用は確かにはるかに簡単です。namespaceURIparsererror

于 2022-01-23T17:06:48.077 に答える
0

現在のブラウザでは、不正な形式のXMLが与えられた場合、DOMParserは2つの可能な動作をするように見えます。

  1. 結果のドキュメントを完全に破棄します—<parsererror>エラーの詳細を含むドキュメントを返します。FirefoxとEdgeは常にこのアプローチを採用しているようです。Chromeファミリーのブラウザは、ほとんどの場合これを行います。

  2. <parsererror>ルート要素の最初の子として1つ余分に挿入された結果のドキュメントを返します。Chromeのパーサーは、ソースXMLでエラーが見つかったにもかかわらず、ルート要素を生成できる場合にこれを行います。挿入され<parsererror>た名前空間がある場合とない場合があります。コメントなどを含め、ドキュメントの残りの部分はそのまま残っているようです。xml_errors.ccを参照してください—を検索してくださいXMLErrors::InsertErrorMessageBlock

(1)の場合、エラーを検出する方法は、ソース文字列にノードを追加して解析し、結果のドキュメントにノードが存在するかどうかを確認してから削除することです。私の知る限り、結果に影響を与える可能性なしにこれを実現する唯一の方法は、ソースの最後に処理命令またはコメントを追加することです。

例:

let key = `a`+Math.random().toString(32);

let doc = (new DOMParser).parseFromString(src+`<?${key}?>`, `application/xml`);

let lastNode = doc.lastChild;
if (!(lastNode instanceof ProcessingInstruction)
    || lastNode.target !== key
    || lastNode.data !== ``)
{
    /* the XML was malformed */
} else {
    /* the XML was well-formed */
    doc.removeChild(lastNode);
}

ケース(2)が発生した場合、上記の手法ではエラーが検出されないため、別の手順が必要です。

<parsererror>ソース内のさまざまな場所で複数のエラーが見つかった場合でも、1つだけが挿入されるという事実を活用できます。ソース文字列を再度解析することにより、この時点で構文エラーが追加され、(2)動作がトリガーされたことを確認してから、<parsererror>要素の数が変更されたかどうかを確認できます。変更されていない場合、最初のparseFromString結果にはすでにtrueが含まれています<parsererror>

例:

let errCount = doc.documentElement.getElementsByTagName(`parsererror`).length;
if (errCount !== 0) {
    let doc2 = parser.parseFromString(src+`<?`, `application/xml`);
    if (doc2.documentElement.getElementsByTagName(`parsererror`).length === errCount) {
        /* the XML was malformed */
    }
}

このアプローチを検証するためのテストページをまとめました:https ://github.com/Cauterite/domparser-tests 。

XML W3C Conformance Test Suite全体に加えて、いくつかの追加サンプルに対してテストを行い、<parsererror>要素を含むドキュメントとDOMParserによって発行された実際のエラーを区別できることを確認します。無効なUnicodeシーケンスが含まれているため、除外されるテストケースはごくわずかです。

XMLHttpRequest.responseXML明確にするために、結果が特定のドキュメントと同じであるかどうかをテストしているだけです。

https://cauterite.github.io/domparser-tests/index.htmlで自分でテストを実行できますが、ECMAScript2018を使用していることに注意してください。

これを書いている時点では、Android上のFirefox、Chrome、Safari、Firefoxの最近のバージョンですべてのテストに合格しています。EdgeおよびPrestoベースのOperaは、DOMParsersがFirefoxのように動作するように見えるため合格する必要があり、現在のOperaはChromiumのフォークであるため合格する必要があります。


反例や改善の可能性があれば教えてください。

怠惰な人のために、ここに完全な機能があります:

const tryParseXml = function(src) {
    /* returns an XMLDocument, or null if `src` is malformed */

    let key = `a`+Math.random().toString(32);

    let parser = new DOMParser;

    let doc = null;
    try {
        doc = parser.parseFromString(
            src+`<?${key}?>`, `application/xml`);
    } catch (_) {}

    if (!(doc instanceof XMLDocument)) {
        return null;
    }

    let lastNode = doc.lastChild;
    if (!(lastNode instanceof ProcessingInstruction)
        || lastNode.target !== key
        || lastNode.data !== ``)
    {
        return null;
    }

    doc.removeChild(lastNode);

    let errElemCount =
        doc.documentElement.getElementsByTagName(`parsererror`).length;
    if (errElemCount !== 0) {
        let errDoc = null;
        try {
            errDoc = parser.parseFromString(
                src+`<?`, `application/xml`);
        } catch (_) {}

        if (!(errDoc instanceof XMLDocument)
            || errDoc.documentElement.getElementsByTagName(`parsererror`).length
                === errElemCount)
        {
            return null;
        }
    }

    return doc;
}
于 2019-04-19T04:17:27.893 に答える