2012-07-19 5 views
22

Sembra che tutti i principali browser implementano l'API DOMParser in modo che XML può essere analizzato in un DOM e poi interrogato utilizzando XPath, getElementsByTagName, ecc ...Come posso rilevare errori di analisi XML durante l'utilizzo di DOMParser di Javascript in modalità cross-browser?

Tuttavia, rilevando errori di analisi sembra essere più complicato. DOMParser.prototype.parseFromString restituisce sempre un DOM valido. Quando si verifica un errore di analisi, il DOM restituito contiene un elemento <parsererror>, ma è leggermente diverso in ciascun browser principale.

Esempio 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)); 

Risultato in Opera:

radice di DOM è un elemento <parsererror>.

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

Risultato in Firefox: root

di DOM è un elemento <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> 

Risultato in Safari:

L'elemento <root> analizza correttamente, ma contiene un nidificato <parsererror> in uno spazio diverso da Opera e Firefox dell'elemento <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> 

mi manca un modo semplice e cross-browser di rilevare se un errore di analisi si è verificato in qualsiasi parte del documento XML? O devo interrogare il DOM per ciascuno dei possibili elementi <parsererror> che potrebbero essere generati da diversi browser?

+0

È sufficiente chiamare '.getElementsByTagName (" parseerror ")' sul nodo DOM radice e presupporre che si sia verificato un errore se la lunghezza dell'elenco dei nodi restituiti è maggiore di zero? – Pointy

+3

Tecnicamente il documento XML che sto analizzando potrebbe contenere '' elementi, ma è ancora totalmente XML valido (gli elementi sarebbero da un diverso spazio dei nomi) Quindi dovrei effettuare più chiamate a '.getElementsByTagNameNS (spazio dei nomi, 'parsererror ') 'per gli URI' namespace' da ciascun browser. – cspotcode

+0

Hmm. Bene, le specifiche HTML5 per questo sono frammentarie, per non dire altro. – Pointy

risposta

13

Questa è la soluzione migliore che ho trovato.

Tentativo di analizzare una stringa XML intenzionalmente non valida e osservare lo spazio dei nomi dell'elemento <parsererror> risultante. Quindi, durante l'analisi dell'XML effettivo, posso utilizzare getElementsByTagNameNS per rilevare lo stesso tipo di elemento <parsererror> e generare un 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; 
}; 

Si noti che questa soluzione non include l'involucro speciale necessario per Internet Explorer. Tuttavia, le cose sono molto più semplici in IE. XML viene analizzato con un metodo loadXML che restituisce true o false se l'analisi ha avuto esito positivo o negativo, rispettivamente. Vedi http://www.w3schools.com/xml/xml_parser.asp per un esempio.

+0

Sto ottenendo quanto segue sia con questa che con la funzione di Rast di seguito: TypeError: Impossibile leggere la proprietà 'namespaceURI' di undefined –

9

Quando sono venuto qui la prima volta, mi upvoted risposta originale (da cspotcode), tuttavia, non funziona in Firefox. Lo spazio dei nomi risultante è sempre "null" a causa della struttura del documento prodotto. Ho fatto una piccola ricerca (controlla il codice here).L'idea è quella di utilizzare non

invalidXml.childNodes[0].namespaceURI 

ma

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

E quindi selezionare elemento "ParserError" di namespace come in risposta originale. Tuttavia, se si dispone di un documento XML valido con il tag <parsererror> nello stesso spazio dei nomi utilizzato dal browser, si finisce con un falso allarme. Quindi, ecco un'euristica per verificare se il vostro XML analizzato correttamente:

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; 
} 

Perché non implementare eccezioni in DOMParser?

cosa interessante da segnalare nel contesto attuale: se si tenta di ottenere file XML con XMLHttpRequest, analizzato DOM verrà memorizzato in responseXML proprietà o null, se il contenuto del file XML non è valido. Non un'eccezione, non parsererror o un altro indicatore specifico. Basta null.

+1

PhantomJS richiede un trattamento speciale. Ho provato a riflettere questo nel modificare la prima risposta. – cburgmer

Problemi correlati