Skip to content

Instantly share code, notes, and snippets.

@westc
Last active June 7, 2022 21:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westc/2cfa3eb3f59faf246379086a9acadfa3 to your computer and use it in GitHub Desktop.
Save westc/2cfa3eb3f59faf246379086a9acadfa3 to your computer and use it in GitHub Desktop.
Convert an XML document or an XML document string into an object that can represent in JSON.
var parseXML = (function() {
/**
* @name parseXML
* @param {XMLDocument|Element|string} xml
* The XML document, the element or the string that represents the XML
* document or the element that should be converted to an object that can
* represent it.
* @returns {parseXML__Element}
*/
function parseXML(xml) {
if ('string' === typeof xml) {
xml = (new DOMParser()).parseFromString(xml.trim(), 'text/xml');
}
// Create the return object
var obj = {
name: xml.nodeName,
type: TYPE_NAMES_BY_ID[xml.nodeType],
};
if (obj.type === 'ELEMENT_NODE') {
if (xml.attributes.length > 0) {
obj.attributes = {};
for (var j = 0; j < xml.attributes.length; j++) {
var attribute = xml.attributes.item(j);
obj.attributes[attribute.nodeName] = attribute.nodeValue;
}
}
}
else if (obj.type === 'TEXT_NODE') {
obj.text = xml.nodeValue;
}
else if (obj.type === 'CDATA_SECTION_NODE') {
obj.cdata = xml.nodeValue;
}
else if (obj.type === 'COMMENT_NODE') {
obj.comment = xml.nodeValue;
}
// Handle children if they exist.
if (xml.hasChildNodes()) {
obj.children = [];
for(var i = 0; i < xml.childNodes.length; i++) {
obj.children.push(parseXML(xml.childNodes.item(i)));
}
if (obj.children.length === 1) {
var child = obj.children[0];
if (child.type === 'TEXT_NODE') {
obj.text = child.text;
delete obj.children;
}
else if (child.type === 'CDATA_SECTION_NODE') {
obj.cdata = child.cdata;
delete obj.children;
}
else if (child.type === 'COMMENT_NODE') {
obj.comment = child.comment;
delete obj.children;
}
}
}
return obj;
}
var TYPE_NAMES_BY_ID = {};
for (var k in XMLDocument) {
if (k.endsWith('_NODE') && 'number' === typeof XMLDocument[k]) {
TYPE_NAMES_BY_ID[XMLDocument[k]] = k;
}
}
/**
* @typedef {Object} parseXML__Element
* @property {string} name
* The `nodeName` of the represented node.
* @property {{[k: string]: string}=} attributes
* Optional. If the corresponding node has attributes it will be an object
* where each key/value pair corresponds to an attribute key/value pair for
* the represented node.
* @property {NodeType} type
* The string version of the `nodeType` of the represented node.
* @property {string=} cdata
* Optional. If the node is a cdata section or if the node only contains
* one node which is a cdata section this will contain the `nodeValue` of
* the corresponding cdata section node.
* @property {string=} comment
* Optional. If the node is a comment or if the node only contains one node
* which is a comment this will contain the `nodeValue` of the corresponding
* comment node.
* @property {string=} text
* Optional. If the node is a text or if the node only contains one node
* which is a text this will contain the `nodeValue` of the corresponding
* text node.
*/
/**
* @typedef {"ELEMENT_NODE"|"ATTRIBUTE_NODE"|"TEXT_NODE"|"CDATA_SECTION_NODE"|"ENTITY_REFERENCE_NODE"|"ENTITY_NODE"|"PROCESSING_INSTRUCTION_NODE"|"COMMENT_NODE"|"DOCUMENT_NODE"|"DOCUMENT_TYPE_NODE"|"DOCUMENT_FRAGMENT_NODE"|"NOTATION_NODE"} NodeType
*/
return parseXML;
})();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<style>
svg.bi {
height: 1em;
width: 1em;
filter:
drop-shadow(0.01em 0.01em 0.001em black)
drop-shadow(0 0 0.01em #FFF);
}
div {
font-size: 500px;
width: 3em;
height: 1.5em;
line-height: 1.75em;
text-align: center;
box-shadow: 0 0 0 1px #000;
background-image: radial-gradient(white -25%, hsl(205,100%,50%) 50%, hsl(210,100%,50%) 50%, black 125%);
color: white;
}
.bi-arrow-right {
margin: 0 -0.4em 0 -0.65em;
}
</style>
</head>
<body>
<div>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-filetype-xml" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5L14 4.5ZM3.527 11.85h-.893l-.823 1.439h-.036L.943 11.85H.012l1.227 1.983L0 15.85h.861l.853-1.415h.035l.85 1.415h.908l-1.254-1.992 1.274-2.007Zm.954 3.999v-2.66h.038l.952 2.159h.516l.946-2.16h.038v2.661h.715V11.85h-.8l-1.14 2.596h-.025L4.58 11.85h-.806v3.999h.706Zm4.71-.674h1.696v.674H8.4V11.85h.791v3.325Z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-right" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-filetype-json" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M14 4.5V11h-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5L14 4.5ZM4.151 15.29a1.176 1.176 0 0 1-.111-.449h.764a.578.578 0 0 0 .255.384c.07.049.154.087.25.114.095.028.201.041.319.041.164 0 .301-.023.413-.07a.559.559 0 0 0 .255-.193.507.507 0 0 0 .084-.29.387.387 0 0 0-.152-.326c-.101-.08-.256-.144-.463-.193l-.618-.143a1.72 1.72 0 0 1-.539-.214 1.001 1.001 0 0 1-.352-.367 1.068 1.068 0 0 1-.123-.524c0-.244.064-.457.19-.639.128-.181.304-.322.528-.422.225-.1.484-.149.777-.149.304 0 .564.05.779.152.217.102.384.239.5.41.12.17.186.359.2.566h-.75a.56.56 0 0 0-.12-.258.624.624 0 0 0-.246-.181.923.923 0 0 0-.37-.068c-.216 0-.387.05-.512.152a.472.472 0 0 0-.185.384c0 .121.048.22.144.3a.97.97 0 0 0 .404.175l.621.143c.217.05.406.12.566.211a1 1 0 0 1 .375.358c.09.148.135.335.135.56 0 .247-.063.466-.188.656a1.216 1.216 0 0 1-.539.439c-.234.105-.52.158-.858.158-.254 0-.476-.03-.665-.09a1.404 1.404 0 0 1-.478-.252 1.13 1.13 0 0 1-.29-.375Zm-3.104-.033a1.32 1.32 0 0 1-.082-.466h.764a.576.576 0 0 0 .074.27.499.499 0 0 0 .454.246c.19 0 .33-.055.422-.164.091-.11.137-.265.137-.466v-2.745h.791v2.725c0 .44-.119.774-.357 1.005-.237.23-.565.345-.985.345a1.59 1.59 0 0 1-.568-.094 1.145 1.145 0 0 1-.407-.266 1.14 1.14 0 0 1-.243-.39Zm9.091-1.585v.522c0 .256-.039.47-.117.641a.862.862 0 0 1-.322.387.877.877 0 0 1-.47.126.883.883 0 0 1-.47-.126.87.87 0 0 1-.32-.387 1.55 1.55 0 0 1-.117-.641v-.522c0-.258.039-.471.117-.641a.87.87 0 0 1 .32-.387.868.868 0 0 1 .47-.129c.177 0 .333.043.47.129a.862.862 0 0 1 .322.387c.078.17.117.383.117.641Zm.803.519v-.513c0-.377-.069-.701-.205-.973a1.46 1.46 0 0 0-.59-.63c-.253-.146-.559-.22-.916-.22-.356 0-.662.074-.92.22a1.441 1.441 0 0 0-.589.628c-.137.271-.205.596-.205.975v.513c0 .375.068.699.205.973.137.271.333.48.589.626.258.145.564.217.92.217.357 0 .663-.072.917-.217.256-.146.452-.355.589-.626.136-.274.205-.598.205-.973Zm1.29-.935v2.675h-.746v-3.999h.662l1.752 2.66h.032v-2.66h.75v4h-.656l-1.761-2.676h-.032Z"/>
</svg>
</div>
</body>
</html>
{
// Title of the IO App
"title": "parseXML() - XML To JSON",
// Description of the IO App
"description": "Convert an XML document into a JSON representation.",
// Name of the function that takes the input string and produces the output string.
"transform": "convert",
// Where to find the code that will be eval'd.
"files": ["parseXML.js", "~io-functions.js"],
// Input setup
"input": { "label": "Input XML", "language": "xml" },
// Output setup
"output": { "label": "Output JSON", "language": "json" },
// Type of IO App
// "type": "split"
}
function convert(input) {
return JSON.stringify(parseXML(input), null, '\t');
}
@westc
Copy link
Author

westc commented Jun 7, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment