Skip to content

Instantly share code, notes, and snippets.

@gocha
Last active September 19, 2018 08:55
Show Gist options
  • Save gocha/3db24b6c4965a857bba8e5e67f87c269 to your computer and use it in GitHub Desktop.
Save gocha/3db24b6c4965a857bba8e5e67f87c269 to your computer and use it in GitHub Desktop.
Parses an XML document or element into a JavaScript Object (JSON)
/**
* 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