A post from three years ago detailing how to implement selectSingleNode for XML documents in a cross-browser friendly manner is still getting a good number of hits. Which I guess shows that developers still need to manipulate XML in browsers, even with the increasing popularity of JSON. When we started to rewrite our desktop app on the web, JSON seemed like the obvious choice, but we use XPath in a big way and we wanted our web app to be compatible with our desktop app, so we stuck with XML, which meant having to deal with the different ways XML is supported in different browsers. So we now have a reasonably well featured cross browser XML parser, the source of which you’ll find below.
A couple of things to note, this probably works in IE8 and below but I’ve never tested it since our app needs at least IE9. Also, the code is TypeScript rather than Javascript, since TypeScript is slightly less insane…
// stop TypeScript complaining about stuff we don't have definitions for interface Window { DOMParser; } declare var XPathResult; class XmlWrapper { private xmlDoc: any; constructor(xml: string) { try { // try Internet Explorer first. Although later versions have DOMParser, they don't implement evaluate this.xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); this.xmlDoc.async = false; this.xmlDoc.loadXML(xml); this.xmlDoc.setProperty("SelectionLanguage", "XPath"); } catch (ex) { if (window.DOMParser) { var parser = new DOMParser(); this.xmlDoc = parser.parseFromString(xml, "text/xml"); } else { throw new Error("Can't find an XML parser!"); } } } public selectSingleElement(xmlNode, elementPath: string): Element { return <Element>this.selectSingleNode(xmlNode, elementPath); } public selectSingleNode(xmlNode, elementPath: string): Node { if (xmlNode == null) { xmlNode = this.xmlDoc; } if (this.xmlDoc.evaluate) { var doc = xmlNode.ownerDocument; if (doc == null) { doc = xmlNode; } var nodes = doc.evaluate(elementPath, xmlNode, null, XPathResult.ANY_TYPE, null); var results = nodes.iterateNext(); return results; } else { return xmlNode.selectSingleNode(elementPath); } } public selectElements(xmlNode, elementPath: string): Element[] { return <Element[]>this.selectNodes(xmlNode, elementPath); } public selectNodes(xmlNode, elementPath: string): Node[] { if (xmlNode == null) { xmlNode = this.xmlDoc; } if (this.xmlDoc.evaluate) { var doc = xmlNode.ownerDocument; if (doc == null) { doc = xmlNode; } var resultsArray = []; var results = doc.evaluate(elementPath, xmlNode, null, XPathResult.ANY_TYPE, null); var thisElement = results.iterateNext(); while (thisElement) { resultsArray.push(thisElement); thisElement = results.iterateNext(); } return resultsArray; } else { return xmlNode.selectNodes(elementPath); } } get documentElement(): Element { return <Element>(this.xmlDoc.documentElement); } public xml(): string { // for IE if (this.xmlDoc.documentElement.xml) { return this.xmlDoc.documentElement.xml; } // Chrome, FireFox if (this.xmlDoc.documentElement.outerHTML) { return this.xmlDoc.documentElement.outerHTML; } // Safari return (new XMLSerializer()).serializeToString(this.xmlDoc.documentElement); } public static getNodeText(node: Node): string { if (node == null) { return ""; } var value: string = node.text; if (node.textContent) { value = node.textContent; } return value; } public static setNodeText(node: Node, value: string): void { if (node == null) { return; } if (node.text !== undefined) { node.text = value; } if (node.textContent !== undefined) { node.textContent = value; } } }
Thursday, March 05, 2015
A cross browser XML parser
Friday, April 06, 2012
Cross browser selectSingleNode for XML in Javascript
Internet Explorer has a non-standard method available in XML documents returned from AJAX calls called selectSingleNode. Pass in some XPath and it returns the first node that matches the XPath. Other browsers have ways of doing the same thing, but they are more long winded. So in short, I like the IE implementation and since I don’t fiddle with XML in JavaScript that often I often forget that selectSingleNode is not supported on all browsers (and if you’re wondering why I use IE as my primary browser, it’s because it’s still, just about, the most popular browser out there).
So here’s a cross browser version of selectSingleNode (not my own work, copy and pasted from somewhere I can’t remember on the web)
function SelectSingleNode(xmlDoc, elementPath) { if (xmlDoc.evaluate) { var nodes = xmlDoc.evaluate(elementPath, xmlDoc, null, XPathResult.ANY_TYPE, null); var results = nodes.iterateNext(); return results; } else return xmlDoc.selectSingleNode(elementPath); }
Update – This no longer works in IE10, since selectSingleNode has been removed from the XML document returned from AJAX calls. This can be worked around by setting the response type of the XmlHttpRequest, like so
xhr.responseType = 'msxml-document';
Update – There’s a more fully featured version of this now available