JsonXml.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /*
  2. The below work is licensed under Creative Commons GNU LGPL License.
  3. Original work:
  4. License: http://creativecommons.org/licenses/LGPL/2.1/
  5. Author: Stefan Goessner/2006
  6. Web: http://goessner.net/
  7. Modifications made:
  8. Version: 0.9-p5
  9. Description: Restructured code, JSLint validated (no strict whitespaces),
  10. added handling of empty arrays, empty strings, and int/floats values.
  11. Author: Michael Schøler/2008-01-29
  12. Web: http://michael.hinnerup.net/blog/2008/01/26/converting-json-to-xml-and-xml-to-json/
  13. Description: json2xml added support to convert functions as CDATA
  14. so it will be easy to write characters that cause some problems when convert
  15. Author: Tony Tomov
  16. */
  17. /*global alert */
  18. var xmlJsonClass = {
  19. // Param "xml": Element or document DOM node.
  20. // Param "tab": Tab or indent string for pretty output formatting omit or use empty string "" to supress.
  21. // Returns: JSON string
  22. xml2json: function(xml, tab) {
  23. if (xml.nodeType === 9) {
  24. // document node
  25. xml = xml.documentElement;
  26. }
  27. var nws = this.removeWhite(xml);
  28. var obj = this.toObj(nws);
  29. var json = this.toJson(obj, xml.nodeName, "\t");
  30. return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
  31. },
  32. // Param "o": JavaScript object
  33. // Param "tab": tab or indent string for pretty output formatting omit or use empty string "" to supress.
  34. // Returns: XML string
  35. json2xml: function(o, tab) {
  36. var toXml = function(v, name, ind) {
  37. var xml = "";
  38. var i, n;
  39. if (v instanceof Array) {
  40. if (v.length === 0) {
  41. xml += ind + "<"+name+">__EMPTY_ARRAY_</"+name+">\n";
  42. }
  43. else {
  44. for (i = 0, n = v.length; i < n; i += 1) {
  45. var sXml = ind + toXml(v[i], name, ind+"\t") + "\n";
  46. xml += sXml;
  47. }
  48. }
  49. }
  50. else if (typeof(v) === "object") {
  51. var hasChild = false;
  52. xml += ind + "<" + name;
  53. var m;
  54. for (m in v) if (v.hasOwnProperty(m)) {
  55. if (m.charAt(0) === "@") {
  56. xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
  57. }
  58. else {
  59. hasChild = true;
  60. }
  61. }
  62. xml += hasChild ? ">" : "/>";
  63. if (hasChild) {
  64. for (m in v) if (v.hasOwnProperty(m)) {
  65. if (m === "#text") {
  66. xml += v[m];
  67. }
  68. else if (m === "#cdata") {
  69. xml += "<![CDATA[" + v[m] + "]]>";
  70. }
  71. else if (m.charAt(0) !== "@") {
  72. xml += toXml(v[m], m, ind+"\t");
  73. }
  74. }
  75. xml += (xml.charAt(xml.length - 1) === "\n" ? ind : "") + "</" + name + ">";
  76. }
  77. }
  78. else if (typeof(v) === "function") {
  79. xml += ind + "<" + name + ">" + "<![CDATA[" + v + "]]>" + "</" + name + ">";
  80. }
  81. else {
  82. if (v === undefined ) { v = ""; }
  83. if (v.toString() === "\"\"" || v.toString().length === 0) {
  84. xml += ind + "<" + name + ">__EMPTY_STRING_</" + name + ">";
  85. }
  86. else {
  87. xml += ind + "<" + name + ">" + v.toString() + "</" + name + ">";
  88. }
  89. }
  90. return xml;
  91. };
  92. var xml = "";
  93. var m;
  94. for (m in o) if (o.hasOwnProperty(m)) {
  95. xml += toXml(o[m], m, "");
  96. }
  97. return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, "");
  98. },
  99. // Internal methods
  100. toObj: function(xml) {
  101. var o = {};
  102. var FuncTest = /function/i;
  103. if (xml.nodeType === 1) {
  104. // element node ..
  105. if (xml.attributes.length) {
  106. // element with attributes ..
  107. var i;
  108. for (i = 0; i < xml.attributes.length; i += 1) {
  109. o["@" + xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue || "").toString();
  110. }
  111. }
  112. if (xml.firstChild) {
  113. // element has child nodes ..
  114. var textChild = 0, cdataChild = 0, hasElementChild = false;
  115. var n;
  116. for (n = xml.firstChild; n; n = n.nextSibling) {
  117. if (n.nodeType === 1) {
  118. hasElementChild = true;
  119. }
  120. else if (n.nodeType === 3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) {
  121. // non-whitespace text
  122. textChild += 1;
  123. }
  124. else if (n.nodeType === 4) {
  125. // cdata section node
  126. cdataChild += 1;
  127. }
  128. }
  129. if (hasElementChild) {
  130. if (textChild < 2 && cdataChild < 2) {
  131. // structured element with evtl. a single text or/and cdata node ..
  132. this.removeWhite(xml);
  133. for (n = xml.firstChild; n; n = n.nextSibling) {
  134. if (n.nodeType === 3) {
  135. // text node
  136. o["#text"] = this.escape(n.nodeValue);
  137. }
  138. else if (n.nodeType === 4) {
  139. // cdata node
  140. if (FuncTest.test(n.nodeValue)) {
  141. o[n.nodeName] = [o[n.nodeName], n.nodeValue];
  142. } else {
  143. o["#cdata"] = this.escape(n.nodeValue);
  144. }
  145. }
  146. else if (o[n.nodeName]) {
  147. // multiple occurence of element ..
  148. if (o[n.nodeName] instanceof Array) {
  149. o[n.nodeName][o[n.nodeName].length] = this.toObj(n);
  150. }
  151. else {
  152. o[n.nodeName] = [o[n.nodeName], this.toObj(n)];
  153. }
  154. }
  155. else {
  156. // first occurence of element ..
  157. o[n.nodeName] = this.toObj(n);
  158. }
  159. }
  160. }
  161. else {
  162. // mixed content
  163. if (!xml.attributes.length) {
  164. o = this.escape(this.innerXml(xml));
  165. }
  166. else {
  167. o["#text"] = this.escape(this.innerXml(xml));
  168. }
  169. }
  170. }
  171. else if (textChild) {
  172. // pure text
  173. if (!xml.attributes.length) {
  174. o = this.escape(this.innerXml(xml));
  175. if (o === "__EMPTY_ARRAY_") {
  176. o = "[]";
  177. } else if (o === "__EMPTY_STRING_") {
  178. o = "";
  179. }
  180. }
  181. else {
  182. o["#text"] = this.escape(this.innerXml(xml));
  183. }
  184. }
  185. else if (cdataChild) {
  186. // cdata
  187. if (cdataChild > 1) {
  188. o = this.escape(this.innerXml(xml));
  189. }
  190. else {
  191. for (n = xml.firstChild; n; n = n.nextSibling) {
  192. if(FuncTest.test(xml.firstChild.nodeValue)) {
  193. o = xml.firstChild.nodeValue;
  194. break;
  195. } else {
  196. o["#cdata"] = this.escape(n.nodeValue);
  197. }
  198. }
  199. }
  200. }
  201. }
  202. if (!xml.attributes.length && !xml.firstChild) {
  203. o = null;
  204. }
  205. }
  206. else if (xml.nodeType === 9) {
  207. // document.node
  208. o = this.toObj(xml.documentElement);
  209. }
  210. else {
  211. alert("unhandled node type: " + xml.nodeType);
  212. }
  213. return o;
  214. },
  215. toJson: function(o, name, ind, wellform) {
  216. if(wellform === undefined) wellform = true;
  217. var json = name ? ("\"" + name + "\"") : "", tab = "\t", newline = "\n";
  218. if(!wellform) {
  219. tab= ""; newline= "";
  220. }
  221. if (o === "[]") {
  222. json += (name ? ":[]" : "[]");
  223. }
  224. else if (o instanceof Array) {
  225. var n, i, ar=[];
  226. for (i = 0, n = o.length; i < n; i += 1) {
  227. ar[i] = this.toJson(o[i], "", ind + tab, wellform);
  228. }
  229. json += (name ? ":[" : "[") + (ar.length > 1 ? (newline + ind + tab + ar.join(","+newline + ind + tab) + newline + ind) : ar.join("")) + "]";
  230. }
  231. else if (o === null) {
  232. json += (name && ":") + "null";
  233. }
  234. else if (typeof(o) === "object") {
  235. var arr = [], m;
  236. for (m in o) {
  237. if (o.hasOwnProperty(m)) {
  238. arr[arr.length] = this.toJson(o[m], m, ind + tab, wellform);
  239. }
  240. }
  241. json += (name ? ":{" : "{") + (arr.length > 1 ? (newline + ind + tab + arr.join(","+newline + ind + tab) + newline + ind) : arr.join("")) + "}";
  242. }
  243. else if (typeof(o) === "string") {
  244. /*
  245. var objRegExp = /(^-?\d+\.?\d*$)/;
  246. var FuncTest = /function/i;
  247. var os = o.toString();
  248. if (objRegExp.test(os) || FuncTest.test(os) || os==="false" || os==="true") {
  249. // int or float
  250. json += (name && ":") + "\"" +os + "\"";
  251. }
  252. else {
  253. */
  254. json += (name && ":") + "\"" + o.replace(/\\/g,'\\\\').replace(/\"/g,'\\"') + "\"";
  255. //}
  256. }
  257. else {
  258. json += (name && ":") + o.toString();
  259. }
  260. return json;
  261. },
  262. innerXml: function(node) {
  263. var s = "";
  264. if ("innerHTML" in node) {
  265. s = node.innerHTML;
  266. }
  267. else {
  268. var asXml = function(n) {
  269. var s = "", i;
  270. if (n.nodeType === 1) {
  271. s += "<" + n.nodeName;
  272. for (i = 0; i < n.attributes.length; i += 1) {
  273. s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\"";
  274. }
  275. if (n.firstChild) {
  276. s += ">";
  277. for (var c = n.firstChild; c; c = c.nextSibling) {
  278. s += asXml(c);
  279. }
  280. s += "</" + n.nodeName + ">";
  281. }
  282. else {
  283. s += "/>";
  284. }
  285. }
  286. else if (n.nodeType === 3) {
  287. s += n.nodeValue;
  288. }
  289. else if (n.nodeType === 4) {
  290. s += "<![CDATA[" + n.nodeValue + "]]>";
  291. }
  292. return s;
  293. };
  294. for (var c = node.firstChild; c; c = c.nextSibling) {
  295. s += asXml(c);
  296. }
  297. }
  298. return s;
  299. },
  300. escape: function(txt) {
  301. return txt.replace(/[\\]/g, "\\\\").replace(/[\"]/g, '\\"').replace(/[\n]/g, '\\n').replace(/[\r]/g, '\\r');
  302. },
  303. removeWhite: function(e) {
  304. e.normalize();
  305. var n;
  306. for (n = e.firstChild; n; ) {
  307. if (n.nodeType === 3) {
  308. // text node
  309. if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) {
  310. // pure whitespace text node
  311. var nxt = n.nextSibling;
  312. e.removeChild(n);
  313. n = nxt;
  314. }
  315. else {
  316. n = n.nextSibling;
  317. }
  318. }
  319. else if (n.nodeType === 1) {
  320. // element node
  321. this.removeWhite(n);
  322. n = n.nextSibling;
  323. }
  324. else {
  325. // any other node
  326. n = n.nextSibling;
  327. }
  328. }
  329. return e;
  330. }
  331. };