Last active
September 19, 2018 08:55
-
-
Save gocha/3db24b6c4965a857bba8e5e67f87c269 to your computer and use it in GitHub Desktop.
Parses an XML document or element into a JavaScript Object (JSON)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Parses an XML document or element into a JavaScript Object. | |
* | |
* To turn the object into JSON, use JSON.stringify(). | |
* | |
* This function is heavily inspired by: | |
* - [Converting Between XML and JSON]{@link http://www.xml.com/pub/a/2006/05/31/converting-between-xml-and-json.html} | |
* - [xmltodict]{@link https://github.com/martinblech/xmltodict} Python library. | |
* Thank you very much for both of two. | |
* | |
* @param {Element | Document} root An XML document or element to be parsed. | |
* @return {Object} A JavaScript Object. | |
* @license Public Domain. | |
*/ | |
function xmlToObject(root) { | |
if (root.nodeType !== Node.DOCUMENT_NODE && root.nodeType !== Node.ELEMENT_NODE) { | |
return undefined; | |
} | |
root.normalize(); | |
let obj = {}; | |
// Add attributes first. | |
if (root.nodeType === Node.ELEMENT_NODE && root.hasAttributes()) { | |
for (let attribute of root.attributes) { | |
obj["@" + attribute.name] = attribute.value; | |
} | |
} | |
// Add child elements. | |
for (let element of root.children) { | |
let name = element.nodeName; | |
let value = xmlToObject(element); | |
if (obj.hasOwnProperty(name)) { | |
if (Array.isArray(obj[name])) { | |
obj[name].push(value); | |
} else { | |
obj[name] = [obj[name], value]; | |
} | |
} else { | |
obj[name] = value; | |
} | |
} | |
if (root.nodeType === Node.ELEMENT_NODE) { | |
// Get text content from child text-like elements (text and CDATA). | |
// | |
// Unlike Stefan Goessner's original parser, this parser will not add elements in mixed content to the text. | |
// Also, this parser doesn't parse CDATA section separately. | |
// The value of CDATA node will simply be concatenated to the text. | |
const textNodes = Array.prototype.filter.call(root.childNodes, | |
it => it.nodeType === Node.CDATA_SECTION_NODE | |
|| (it.nodeType === Node.TEXT_NODE && /[^\t\n\r ]/.test(it.nodeValue))) | |
.map(it => it.nodeValue); | |
const text = textNodes.length > 0 ? textNodes.join('') : null; | |
// Add the text content at last. | |
if (Object.getOwnPropertyNames(obj).length === 0) { | |
obj = text; | |
} else if (text !== null) { | |
obj["#text"] = text; | |
} | |
} | |
return obj; | |
}; | |
// const text = `<e>text</e>`; | |
// const document = (new DOMParser()).parseFromString(text, "application/xml"); | |
// console.log(JSON.stringify(xmlToObject(document))); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment