123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- /*
- Copyright 2011-2013 Abdulla Abdurakhmanov
- Original sources are available at https://code.google.com/p/x2js/
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- var DOMParser = require('@xmldom/xmldom').DOMParser;
- var x2js = function (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 = new Object;
- 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 = new Object;
- result.__cnt=0;
- var nodeChildren = node.childNodes;
- // Children nodes
- for(var cidx=0; cidx <nodeChildren.length; cidx++) {
- var child = nodeChildren.item(cidx); // nodeChildren[cidx];
- var 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;
- var isIEParser = false;
- if (xmlDocStr === undefined) {
- return null;
- }
- var xmlDoc;
- if (DOMParser) {
- var parser=new 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");
- 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;
- };
- };
- var xml2json = function (str) {
- if (!str) return null;
- var parser = new DOMParser();
- var xmlDoc = parser.parseFromString(str, "text/xml");
- var x2jsObj = new x2js();
- var data = x2jsObj.xml2json(xmlDoc);
- if (data.html && data.getElementsByTagName('parsererror').length) {
- return null;
- } else {
- return data;
- }
- };
- var json2xml = function (data) {
- var x2jsObj = new x2js();
- return x2jsObj.json2xml(data);
- };
- module.exports = xml2json;
|