node-x2js
Version:
x2js - XML to JSON and vice versa for JavaScript
487 lines (415 loc) • 17 kB
JavaScript
;
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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');else return str;
}
function unescapeXmlChars(str) {
return str.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/&/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"];