UNPKG

node-x2js

Version:

x2js - XML to JSON and vice versa for JavaScript

487 lines (415 loc) 17 kB
"use strict"; exports.__esModule = true; /* based on https://github.com/abdmob/x2js/blob/master/xml2json.js, VERSION = "1.2.0" */ /*eslint-disable quotes*/ /*eslint-disable prefer-template*/ /*eslint-disable camelcase*/ /*eslint-disable eqeqeq*/ /*eslint-disable prefer-const*/ /*eslint-disable space-unary-ops*/ /*eslint-disable semi-spacing*/ /*eslint-disable no-unused-vars*/ var X2JS = function X2JS(config) { 'use strict'; var VERSION = "1.2.0"; config = config || {}; initConfigDefaults(); initRequiredPolyfills(); function initConfigDefaults() { if (config.escapeMode === undefined) { config.escapeMode = true; } config.attributePrefix = config.attributePrefix || "_"; config.arrayAccessForm = config.arrayAccessForm || "none"; config.emptyNodeForm = config.emptyNodeForm || "text"; if (config.enableToStringFunc === undefined) { config.enableToStringFunc = true; } config.arrayAccessFormPaths = config.arrayAccessFormPaths || []; if (config.skipEmptyTextNodesForObj === undefined) { config.skipEmptyTextNodesForObj = true; } if (config.stripWhitespaces === undefined) { config.stripWhitespaces = true; } config.datetimeAccessFormPaths = config.datetimeAccessFormPaths || []; if (config.useDoubleQuotes === undefined) { config.useDoubleQuotes = false; } config.xmlElementsFilter = config.xmlElementsFilter || []; config.jsonPropertiesFilter = config.jsonPropertiesFilter || []; if (config.keepCData === undefined) { config.keepCData = false; } } var DOMNodeTypes = { ELEMENT_NODE: 1, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, COMMENT_NODE: 8, DOCUMENT_NODE: 9 }; function initRequiredPolyfills() {} function getNodeLocalName(node) { var nodeLocalName = node.localName; if (nodeLocalName == null) // Yeah, this is IE!! nodeLocalName = node.baseName; if (nodeLocalName == null || nodeLocalName == "") // =="" is IE too nodeLocalName = node.nodeName; return nodeLocalName; } function getNodePrefix(node) { return node.prefix; } function escapeXmlChars(str) { if (typeof str == "string") return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');else return str; } function unescapeXmlChars(str) { return str.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, '&'); } function checkInStdFiltersArrayForm(stdFiltersArrayForm, obj, name, path) { var idx = 0; for (; idx < stdFiltersArrayForm.length; idx++) { var filterPath = stdFiltersArrayForm[idx]; if (typeof filterPath === "string") { if (filterPath == path) break; } else if (filterPath instanceof RegExp) { if (filterPath.test(path)) break; } else if (typeof filterPath === "function") { if (filterPath(obj, name, path)) break; } } return idx != stdFiltersArrayForm.length; } function toArrayAccessForm(obj, childName, path) { switch (config.arrayAccessForm) { case "property": if (!(obj[childName] instanceof Array)) obj[childName + "_asArray"] = [obj[childName]];else obj[childName + "_asArray"] = obj[childName]; break; /*case "none": break;*/ } if (!(obj[childName] instanceof Array) && config.arrayAccessFormPaths.length > 0) { if (checkInStdFiltersArrayForm(config.arrayAccessFormPaths, obj, childName, path)) { obj[childName] = [obj[childName]]; } } } function fromXmlDateTime(prop) { // Implementation based up on http://stackoverflow.com/questions/8178598/xml-datetime-to-javascript-date-object // Improved to support full spec and optional parts var bits = prop.split(/[-T:+Z]/g); var d = new Date(bits[0], bits[1] - 1, bits[2]); var secondBits = bits[5].split("\."); d.setHours(bits[3], bits[4], secondBits[0]); if (secondBits.length > 1) d.setMilliseconds(secondBits[1]); // Get supplied time zone offset in minutes if (bits[6] && bits[7]) { var offsetMinutes = bits[6] * 60 + Number(bits[7]); var sign = /\d\d-\d\d:\d\d$/.test(prop) ? '-' : '+'; // Apply the sign offsetMinutes = 0 + (sign == '-' ? -1 * offsetMinutes : offsetMinutes); // Apply offset and local timezone d.setMinutes(d.getMinutes() - offsetMinutes - d.getTimezoneOffset()); } else if (prop.indexOf("Z", prop.length - 1) !== -1) { d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds())); } // d is now a local time equivalent to the supplied time return d; } function checkFromXmlDateTimePaths(value, childName, fullPath) { if (config.datetimeAccessFormPaths.length > 0) { var path = fullPath.split("\.#")[0]; if (checkInStdFiltersArrayForm(config.datetimeAccessFormPaths, value, childName, path)) { return fromXmlDateTime(value); } else return value; } else return value; } function checkXmlElementsFilter(obj, childType, childName, childPath) { if (childType == DOMNodeTypes.ELEMENT_NODE && config.xmlElementsFilter.length > 0) { return checkInStdFiltersArrayForm(config.xmlElementsFilter, obj, childName, childPath); } else return true; } function parseDOMChildren(node, path) { if (node.nodeType == DOMNodeTypes.DOCUMENT_NODE) { var result = {}; var nodeChildren = node.childNodes; // Alternative for firstElementChild which is not supported in some environments for (var cidx = 0; cidx < nodeChildren.length; cidx++) { var child = nodeChildren.item(cidx); if (child.nodeType == DOMNodeTypes.ELEMENT_NODE) { var childName = getNodeLocalName(child); result[childName] = parseDOMChildren(child, childName); } } return result; } else if (node.nodeType == DOMNodeTypes.ELEMENT_NODE) { var _result = {}; _result.__cnt = 0; var _nodeChildren = node.childNodes; var _childName = ''; // Children nodes for (var _cidx = 0; _cidx < _nodeChildren.length; _cidx++) { var _child = _nodeChildren.item(_cidx); // nodeChildren[cidx]; _childName = getNodeLocalName(_child); if (_child.nodeType != DOMNodeTypes.COMMENT_NODE) { var childPath = path + "." + _childName; if (checkXmlElementsFilter(_result, _child.nodeType, _childName, childPath)) { _result.__cnt++; if (_result[_childName] == null) { _result[_childName] = parseDOMChildren(_child, childPath); toArrayAccessForm(_result, _childName, childPath); } else { if (_result[_childName] != null) { if (!(_result[_childName] instanceof Array)) { _result[_childName] = [_result[_childName]]; toArrayAccessForm(_result, _childName, childPath); } } _result[_childName][_result[_childName].length] = parseDOMChildren(_child, childPath); } } } } // Attributes for (var aidx = 0; aidx < node.attributes.length; aidx++) { var attr = node.attributes.item(aidx); // [aidx]; _result.__cnt++; _result[config.attributePrefix + attr.name] = attr.value; } // Node namespace prefix var nodePrefix = getNodePrefix(node); if (nodePrefix != null && nodePrefix != "") { _result.__cnt++; _result.__prefix = nodePrefix; } if (_result["#text"] != null) { _result.__text = _result["#text"]; if (_result.__text instanceof Array) { _result.__text = _result.__text.join("\n"); } //if(config.escapeMode) // result.__text = unescapeXmlChars(result.__text); if (config.stripWhitespaces) _result.__text = _result.__text.trim(); delete _result["#text"]; if (config.arrayAccessForm == "property") delete _result["#text_asArray"]; _result.__text = checkFromXmlDateTimePaths(_result.__text, _childName, path + "." + _childName); } if (_result["#cdata-section"] != null) { _result.__cdata = _result["#cdata-section"]; delete _result["#cdata-section"]; if (config.arrayAccessForm == "property") delete _result["#cdata-section_asArray"]; } if (_result.__cnt == 0 && config.emptyNodeForm == "text") { _result = ''; } else if (_result.__cnt == 1 && _result.__text != null) { _result = _result.__text; } else if (_result.__cnt == 1 && _result.__cdata != null && !config.keepCData) { _result = _result.__cdata; } else if (_result.__cnt > 1 && _result.__text != null && config.skipEmptyTextNodesForObj) { if (config.stripWhitespaces && _result.__text == "" || _result.__text.trim() == "") { delete _result.__text; } } delete _result.__cnt; if (config.enableToStringFunc && (_result.__text != null || _result.__cdata != null)) { _result.toString = function () { return (this.__text != null ? this.__text : '') + (this.__cdata != null ? this.__cdata : ''); }; } return _result; } else if (node.nodeType == DOMNodeTypes.TEXT_NODE || node.nodeType == DOMNodeTypes.CDATA_SECTION_NODE) { return node.nodeValue; } } function startTag(jsonObj, element, attrList, closed) { var resultStr = "<" + (jsonObj != null && jsonObj.__prefix != null ? jsonObj.__prefix + ":" : "") + element; if (attrList != null) { for (var aidx = 0; aidx < attrList.length; aidx++) { var attrName = attrList[aidx]; var attrVal = jsonObj[attrName]; if (config.escapeMode) attrVal = escapeXmlChars(attrVal); resultStr += " " + attrName.substr(config.attributePrefix.length) + "="; if (config.useDoubleQuotes) resultStr += '"' + attrVal + '"';else resultStr += "'" + attrVal + "'"; } } if (!closed) resultStr += ">";else resultStr += "/>"; return resultStr; } function endTag(jsonObj, elementName) { return "</" + (jsonObj.__prefix != null ? jsonObj.__prefix + ":" : "") + elementName + ">"; } function endsWith(str, suffix) { return str.indexOf(suffix, str.length - suffix.length) !== -1; } function jsonXmlSpecialElem(jsonObj, jsonObjField) { if (config.arrayAccessForm == "property" && endsWith(jsonObjField.toString(), "_asArray") || jsonObjField.toString().indexOf(config.attributePrefix) == 0 || jsonObjField.toString().indexOf("__") == 0 || jsonObj[jsonObjField] instanceof Function) return true;else return false; } function jsonXmlElemCount(jsonObj) { var elementsCnt = 0; if (jsonObj instanceof Object) { for (var it in jsonObj) { if (jsonXmlSpecialElem(jsonObj, it)) continue; elementsCnt++; } } return elementsCnt; } function checkJsonObjPropertiesFilter(jsonObj, propertyName, jsonObjPath) { return config.jsonPropertiesFilter.length == 0 || jsonObjPath == "" || checkInStdFiltersArrayForm(config.jsonPropertiesFilter, jsonObj, propertyName, jsonObjPath); } function parseJSONAttributes(jsonObj) { var attrList = []; if (jsonObj instanceof Object) { for (var ait in jsonObj) { if (ait.toString().indexOf("__") == -1 && ait.toString().indexOf(config.attributePrefix) == 0) { attrList.push(ait); } } } return attrList; } function parseJSONTextAttrs(jsonTxtObj) { var result = ""; if (jsonTxtObj.__cdata != null) { result += "<![CDATA[" + jsonTxtObj.__cdata + "]]>"; } if (jsonTxtObj.__text != null) { if (config.escapeMode) result += escapeXmlChars(jsonTxtObj.__text);else result += jsonTxtObj.__text; } return result; } function parseJSONTextObject(jsonTxtObj) { var result = ""; if (jsonTxtObj instanceof Object) { result += parseJSONTextAttrs(jsonTxtObj); } else if (jsonTxtObj != null) { if (config.escapeMode) result += escapeXmlChars(jsonTxtObj);else result += jsonTxtObj; } return result; } function getJsonPropertyPath(jsonObjPath, jsonPropName) { if (jsonObjPath === "") { return jsonPropName; } else return jsonObjPath + "." + jsonPropName; } function parseJSONArray(jsonArrRoot, jsonArrObj, attrList, jsonObjPath) { var result = ""; if (jsonArrRoot.length == 0) { result += startTag(jsonArrRoot, jsonArrObj, attrList, true); } else { for (var arIdx = 0; arIdx < jsonArrRoot.length; arIdx++) { result += startTag(jsonArrRoot[arIdx], jsonArrObj, parseJSONAttributes(jsonArrRoot[arIdx]), false); result += parseJSONObject(jsonArrRoot[arIdx], getJsonPropertyPath(jsonObjPath, jsonArrObj)); result += endTag(jsonArrRoot[arIdx], jsonArrObj); } } return result; } function parseJSONObject(jsonObj, jsonObjPath) { var result = ""; var elementsCnt = jsonXmlElemCount(jsonObj); if (elementsCnt > 0) { for (var it in jsonObj) { if (jsonXmlSpecialElem(jsonObj, it) || jsonObjPath != "" && !checkJsonObjPropertiesFilter(jsonObj, it, getJsonPropertyPath(jsonObjPath, it))) continue; var subObj = jsonObj[it]; var attrList = parseJSONAttributes(subObj); if (subObj == null || subObj == undefined) { result += startTag(subObj, it, attrList, true); } else if (subObj instanceof Object) { if (subObj instanceof Array) { result += parseJSONArray(subObj, it, attrList, jsonObjPath); } else if (subObj instanceof Date) { result += startTag(subObj, it, attrList, false); result += subObj.toISOString(); result += endTag(subObj, it); } else { var subObjElementsCnt = jsonXmlElemCount(subObj); if (subObjElementsCnt > 0 || subObj.__text != null || subObj.__cdata != null) { result += startTag(subObj, it, attrList, false); result += parseJSONObject(subObj, getJsonPropertyPath(jsonObjPath, it)); result += endTag(subObj, it); } else { result += startTag(subObj, it, attrList, true); } } } else { result += startTag(subObj, it, attrList, false); result += parseJSONTextObject(subObj); result += endTag(subObj, it); } } } result += parseJSONTextObject(jsonObj); return result; } this.parseXmlString = function (xmlDocStr) { var isIEParser = window.ActiveXObject || "ActiveXObject" in window; if (xmlDocStr === undefined) { return null; } var xmlDoc = void 0; if (window.DOMParser) { var parser = new window.DOMParser(); var parsererrorNS = null; // IE9+ now is here if (!isIEParser) { try { parsererrorNS = parser.parseFromString("INVALID", "text/xml").getElementsByTagName("parsererror")[0].namespaceURI; } catch (err) { parsererrorNS = null; } } try { xmlDoc = parser.parseFromString(xmlDocStr, "text/xml"); if (parsererrorNS != null && xmlDoc.getElementsByTagNameNS(parsererrorNS, "parsererror").length > 0) { //throw new Error('Error parsing XML: '+xmlDocStr); xmlDoc = null; } } catch (err) { xmlDoc = null; } } else { // IE :( if (xmlDocStr.indexOf("<?") == 0) { xmlDocStr = xmlDocStr.substr(xmlDocStr.indexOf("?>") + 2); } xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); // eslint-disable-line no-undef xmlDoc.async = "false"; xmlDoc.loadXML(xmlDocStr); } return xmlDoc; }; this.asArray = function (prop) { if (prop === undefined || prop == null) return [];else if (prop instanceof Array) return prop;else return [prop]; }; this.toXmlDateTime = function (dt) { if (dt instanceof Date) return dt.toISOString();else if (typeof dt === 'number') return new Date(dt).toISOString();else return null; }; this.asDateTime = function (prop) { if (typeof prop == "string") { return fromXmlDateTime(prop); } else return prop; }; this.xml2json = function (xmlDoc) { return parseDOMChildren(xmlDoc); }; this.xml_str2json = function (xmlDocStr) { var xmlDoc = this.parseXmlString(xmlDocStr); if (xmlDoc != null) return this.xml2json(xmlDoc);else return null; }; this.json2xml_str = function (jsonObj) { return parseJSONObject(jsonObj, ""); }; this.json2xml = function (jsonObj) { var xmlDocStr = this.json2xml_str(jsonObj); return this.parseXmlString(xmlDocStr); }; this.getVersion = function () { return VERSION; }; }; exports.default = X2JS; module.exports = exports["default"];