Created
July 5, 2010 15:47
-
-
Save czue/464479 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
The below work is licensed under Creative Commons GNU LGPL License. | |
Original work: | |
License: http://creativecommons.org/licenses/LGPL/2.1/ | |
Author: Stefan Goessner/2006 | |
Web: http://goessner.net/ | |
Modifications made: | |
Version: 0.9-p5 | |
Description: Restructured code, JSLint validated (no strict whitespaces), | |
added handling of empty arrays, empty strings, and int/floats values. | |
Author: Michael Scholer/2008-01-29 | |
Web: http://michael.hinnerup.net/blog/2008/01/26/converting-json-to-xml-and-xml-to-json/ | |
Version: 0.9.1 | |
Description: Changed DOM XML to E4X XML. | |
Author: Cory Zue ([email protected])/2010-05-29 | |
Web: http://www.dimagi.com/ | |
*/ | |
// this gets used when a text field has both attributes and a text value: | |
// <myvar a="3">bcd</myvar> ==> {"myvar": {"@a": 3, "#text": "bcd"} | |
var VALUE_TAG = "#text"; | |
// this gets used to specify the root object's name | |
// at a top level: | |
// <myvar a="3"><b>cd</b></myvar> ==> {"#type": "myvar", "@a": 3, "b": "cd"} | |
var ROOT_TAG = "#type"; | |
/*global alert */ | |
var e4xmlJsonClass = { | |
// abstract out the logging functionality. it's not beautiful, but | |
// this at leasts provides a single point to change it (you still | |
// have to edit the code, unfortunately) | |
log_func: log, | |
hello: function() { | |
this.log_func("hello world!"); | |
}, | |
// Param "xml": E4X XML object | |
// Returns: JavaScript object | |
xml2obj: function(xml) { | |
var obj = this.toObj(xml); | |
if (xml.name().uri && obj) { | |
obj["@xmlns"] = xml.name().uri; | |
} | |
obj[ROOT_TAG] = xml.name().localName; | |
return obj | |
}, | |
// Param "xml": E4X XML object | |
// Param "tab": Tab or indent string for pretty output formatting omit or use empty string "" to supress. | |
// Returns: JSON string | |
xml2json: function(xml) { | |
var obj = this.xml2obj(xml); | |
var json = this.toJson(obj, xml.name().localName, "\t"); | |
return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}"; | |
}, | |
// Param "o": JavaScript object | |
// Param "tab": tab or indent string for pretty output formatting omit or use empty string "" to supress. | |
// Returns: XML string | |
json2xml: function(o, tab) { | |
var toXml = function(v, name, ind) { | |
var xml = ""; | |
var i, n; | |
if (v instanceof Array) { | |
if (v.length === 0) { | |
xml += ind + "<"+name+">__EMPTY_ARRAY_</"+name+">\n"; | |
} | |
else { | |
for (i = 0, n = v.length; i < n; i += 1) { | |
var sXml = ind + toXml(v[i], name, ind+"\t") + "\n"; | |
xml += sXml; | |
} | |
} | |
} | |
else if (typeof(v) === "object") { | |
var hasChild = false; | |
xml += ind + "<" + name; | |
var m; | |
for (m in v) if (v.hasOwnProperty(m)) { | |
if (m.charAt(0) === "@") { | |
xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\""; | |
} | |
else { | |
hasChild = true; | |
} | |
} | |
xml += hasChild ? ">" : "/>"; | |
if (hasChild) { | |
for (m in v) if (v.hasOwnProperty(m)) { | |
if (m === "#text") { | |
xml += v[m]; | |
} | |
else if (m === "#cdata") { | |
xml += "<![CDATA[" + v[m] + "]]>"; | |
} | |
else if (m.charAt(0) !== "@") { | |
xml += toXml(v[m], m, ind+"\t"); | |
} | |
} | |
xml += (xml.charAt(xml.length - 1) === "\n" ? ind : "") + "</" + name + ">"; | |
} | |
} | |
else { | |
if (v.toString() === "\"\"" || v.toString().length === 0) { | |
xml += ind + "<" + name + ">__EMPTY_STRING_</" + name + ">"; | |
} | |
else { | |
xml += ind + "<" + name + ">" + v.toString() + "</" + name + ">"; | |
} | |
} | |
return xml; | |
}; | |
var xml = ""; | |
var m; | |
for (m in o) if (o.hasOwnProperty(m)) { | |
xml += toXml(o[m], m, ""); | |
} | |
return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, ""); | |
}, | |
logObj: function(obj) { | |
this.log_func("length: " + obj.length()); | |
for (var prop in obj) { | |
val = obj[prop]; | |
if (val) { | |
this.log_func("prop: " + prop + ", name: " + val.name() + | |
", localName: " + val.name().localName + | |
",value: " + val); | |
if (val != obj) { | |
this.logObj(val); | |
} | |
} | |
} | |
}, | |
// Internal methods | |
toObj: function(xml) { | |
this.log_func("processing: " + xml); | |
var attributes = xml.@*; | |
// base case, a simple node | |
if (attributes.length() == 0 && !this.hasChildren(xml)) { | |
return (xml || "").toString(); | |
} | |
var o = {}; | |
// process attributes | |
if (attributes.length() > 0) { | |
this.log_func("found " + attributes.length() + " attributes."); | |
for (var i = 0; i < attributes.length(); i++) { | |
o["@" + attributes[i].name().localName] = (attributes[i] || "").toString(); | |
this.log_func("attribute " + i + ": " + attributes[i].name().localName + ":" + attributes[i]); | |
} | |
} | |
// process children | |
if (this.hasChildren(xml)) { | |
var children = xml.*; | |
this.log_func("found " + children.length() + " children."); | |
for (var i = 0; i < children.length(); i++) { | |
this.log_func("child " + i + ": " + children[i].name() + ":" + children[i]); | |
if (xml.*::[children[i].name().localName].length() > 1) { | |
// there was more than one of these elements. Make it a list. | |
if (!o[children[i].name().localName]) { | |
o[children[i].name().localName] = []; | |
} | |
o[children[i].name().localName][o[children[i].name().localName].length] = this.toObj(children[i]); | |
} else { | |
o[children[i].name().localName] = this.toObj(children[i]); | |
} | |
this.log_func("xml length: " + xml.*::[children[i].name()].length() + " and index: " + xml[children[i].name()]); | |
} | |
} | |
else { | |
// this had attributes, but no children. Default the value to #text | |
o[VALUE_TAG] = (xml || "").toString(); | |
} | |
return o; | |
}, | |
hasChildren: function(xml) { | |
if (xml.*.length() > 0 && !(xml.*.length() == 1 && !xml.*[0].name())) { | |
return true; | |
} | |
return false; | |
}, | |
toJson: function(o, name, ind) { | |
var json = name ? ("\"" + name + "\"") : ""; | |
if (o === "[]") { | |
json += (name ? ":[]" : "[]"); | |
} | |
else if (o instanceof Array) { | |
var n, i; | |
for (i = 0, n = o.length; i < n; i += 1) { | |
o[i] = this.toJson(o[i], "", ind + "\t"); | |
} | |
json += (name ? ":[" : "[") + (o.length > 1 ? ("\n" + ind + "\t" + o.join(",\n" + ind + "\t") + "\n" + ind) : o.join("")) + "]"; | |
} | |
else if (o === null) { | |
json += (name && ":") + "null"; | |
} | |
else if (typeof(o) === "object") { | |
var arr = []; | |
var m; | |
for (m in o) if (o.hasOwnProperty(m)) { | |
arr[arr.length] = this.toJson(o[m], m, ind + "\t"); | |
} | |
json += (name ? ":{" : "{") + (arr.length > 1 ? ("\n" + ind + "\t" + arr.join(",\n" + ind + "\t") + "\n" + ind) : arr.join("")) + "}"; | |
} | |
else if (typeof(o) === "string") { | |
o = o.toString(); | |
var objRegExp = /(^-?\d+\.?\d*$)/; | |
if (objRegExp.test(o)) { | |
// int or float | |
json += (name && ":") + o; | |
} | |
else { | |
json += (name && ":") + "\"" + o + "\""; | |
} | |
} | |
else { | |
json += (name && ":") + o.toString(); | |
} | |
return json; | |
}, | |
innerXml: function(node) { | |
var s = ""; | |
if ("innerHTML" in node) { | |
s = node.innerHTML; | |
} | |
else { | |
var asXml = function(n) { | |
var s = "", i; | |
if (n.nodeType === 1) { | |
s += "<" + n.nodeName; | |
for (i = 0; i < n.attributes.length; i += 1) { | |
s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\""; | |
} | |
if (n.firstChild) { | |
s += ">"; | |
for (var c = n.firstChild; c; c = c.nextSibling) { | |
s += asXml(c); | |
} | |
s += "</" + n.nodeName + ">"; | |
} | |
else { | |
s += "/>"; | |
} | |
} | |
else if (n.nodeType === 3) { | |
s += n.nodeValue; | |
} | |
else if (n.nodeType === 4) { | |
s += "<![CDATA[" + n.nodeValue + "]]>"; | |
} | |
return s; | |
}; | |
for (var c = node.firstChild; c; c = c.nextSibling) { | |
s += asXml(c); | |
} | |
} | |
return s; | |
}, | |
escape: function(txt) { | |
return txt.replace(/[\\]/g, "\\\\").replace(/[\"]/g, '\\"').replace(/[\n]/g, '\\n').replace(/[\r]/g, '\\r'); | |
}, | |
removeWhite: function(e) { | |
e.normalize(); | |
var n; | |
for (n = e.firstChild; n; ) { | |
if (n.nodeType === 3) { | |
// text node | |
if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) { | |
// pure whitespace text node | |
var nxt = n.nextSibling; | |
e.removeChild(n); | |
n = nxt; | |
} | |
else { | |
n = n.nextSibling; | |
} | |
} | |
else if (n.nodeType === 1) { | |
// element node | |
this.removeWhite(n); | |
n = n.nextSibling; | |
} | |
else { | |
// any other node | |
n = n.nextSibling; | |
} | |
} | |
return e; | |
} | |
}; | |
exports.e4xmlJsonClass = e4xmlJsonClass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment