modules/util/jxon.js (118 lines of code) (raw):

export var JXON = new (function () { var sValueProp = 'keyValue', sAttributesProp = 'keyAttributes', sAttrPref = '@', /* you can customize these values */ aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i; function parseText (sValue) { if (rIsNull.test(sValue)) { return null; } if (rIsBool.test(sValue)) { return sValue.toLowerCase() === 'true'; } if (isFinite(sValue)) { return parseFloat(sValue); } if (isFinite(Date.parse(sValue))) { return new Date(sValue); } return sValue; } function EmptyTree () { } EmptyTree.prototype.toString = function () { return 'null'; }; EmptyTree.prototype.valueOf = function () { return null; }; function objectify (vValue) { return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue); } function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) { var nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(), bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2); var sProp, vContent, nLength = 0, sCollectedTxt = '', vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true; if (bChildren) { for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) { oNode = oParentNode.childNodes.item(nItem); if (oNode.nodeType === 4) { /* nodeType is 'CDATASection' (4) */ sCollectedTxt += oNode.nodeValue; } else if (oNode.nodeType === 3) { /* nodeType is 'Text' (3) */ sCollectedTxt += oNode.nodeValue.trim(); } else if (oNode.nodeType === 1 && !oNode.prefix) { /* nodeType is 'Element' (1) */ aCache.push(oNode); } } } var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt); if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; } for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) { sProp = aCache[nElId].nodeName.toLowerCase(); vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr); if (vResult.hasOwnProperty(sProp)) { if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; } vResult[sProp].push(vContent); } else { vResult[sProp] = vContent; nLength++; } } if (bAttributes) { var nAttrLen = oParentNode.attributes.length, sAPrefix = bNesteAttr ? '' : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult; for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) { oAttrib = oParentNode.attributes.item(nAttrib); oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim()); } if (bNesteAttr) { if (bFreeze) { Object.freeze(oAttrParent); } vResult[sAttributesProp] = oAttrParent; nLength -= nAttrLen - 1; } } if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) { vResult[sValueProp] = vBuiltVal; } else if (!bHighVerb && nLength === 0 && sCollectedTxt) { vResult = vBuiltVal; } if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); } aCache.length = nLevelStart; return vResult; } function loadObjTree (oXMLDoc, oParentEl, oParentObj) { var vValue, oChild; if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) { oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */ } else if (oParentObj.constructor === Date) { oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString())); } for (var sName in oParentObj) { vValue = oParentObj[sName]; if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */ if (sName === sValueProp) { if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); } } else if (sName === sAttributesProp) { /* verbosity level is 3 */ for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); } } else if (sName.charAt(0) === sAttrPref) { oParentEl.setAttribute(sName.slice(1), vValue); } else if (vValue.constructor === Array) { for (var nItem = 0; nItem < vValue.length; nItem++) { oChild = oXMLDoc.createElement(sName); loadObjTree(oXMLDoc, oChild, vValue[nItem]); oParentEl.appendChild(oChild); } } else { oChild = oXMLDoc.createElement(sName); if (vValue instanceof Object) { loadObjTree(oXMLDoc, oChild, vValue); } else if (vValue !== null && vValue !== true) { oChild.appendChild(oXMLDoc.createTextNode(vValue.toString())); } oParentEl.appendChild(oChild); } } } this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) { var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 : /* put here the default verbosity level: */ 1; return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3); }; this.unbuild = function (oObjTree) { var oNewDoc = document.implementation.createDocument('', '', null); loadObjTree(oNewDoc, oNewDoc, oObjTree); return oNewDoc; }; this.stringify = function (oObjTree) { return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree)); }; })(); // var myObject = JXON.build(doc); // we got our javascript object! try: alert(JSON.stringify(myObject)); // var newDoc = JXON.unbuild(myObject); // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));