jsonxml.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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 jQuery, alert, define, exports, module, require */
  18. /*jslint browser: true, vars: true, regexp: true, white: true */
  19. (function (global, factory) {
  20. "use strict";
  21. if (typeof define === "function" && define.amd) {
  22. // AMD. Register as an anonymous module.
  23. //console.log("jsonxml AMD");
  24. define([
  25. "jquery"
  26. ], function ($) {
  27. //console.log("jsonxml AMD: define callback");
  28. return factory($, global);
  29. });
  30. } else if (typeof module === "object" && module.exports) {
  31. // Node/CommonJS
  32. //console.log("jsonxml CommonJS, typeof define=" + typeof define + ", define=" + define);
  33. module.exports = function (root, $) {
  34. //console.log("jsonxml CommonJS: in module.exports");
  35. if (!root) {
  36. root = window;
  37. }
  38. //console.log("jsonxml CommonJS: before require('jquery')");
  39. if ($ === undefined) {
  40. // require("jquery") returns a factory that requires window to
  41. // build a jQuery instance, we normalize how we use modules
  42. // that require this pattern but the window provided is a noop
  43. // if it's defined (how jquery works)
  44. $ = typeof window !== "undefined" ?
  45. require("jquery") :
  46. require("jquery")(root);
  47. }
  48. //console.log("jsonxml CommonJS: before factory");
  49. return factory($, root);
  50. };
  51. } else {
  52. // Browser globals
  53. //console.log("jsonxml Browser: before factory");
  54. factory(jQuery, global);
  55. }
  56. }(typeof window !== "undefined" ? window : this, function ($, window) {
  57. "use strict";
  58. // begin module jsonxml
  59. var xmlJsonClass = {
  60. // Param "xml": Element or document DOM node.
  61. // Param "tab": Tab or indent string for pretty output formatting omit or use empty string "" to supress.
  62. // Returns: JSON string
  63. xml2json: function (xml, tab) {
  64. if (xml.nodeType === 9) {
  65. // document node
  66. xml = xml.documentElement;
  67. }
  68. var nws = this.removeWhite(xml),
  69. obj = this.toObj(nws),
  70. json = this.toJson(obj, xml.nodeName, "\t");
  71. return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
  72. },
  73. // Param "o": JavaScript object
  74. // Param "tab": tab or indent string for pretty output formatting omit or use empty string "" to supress.
  75. // Returns: XML string
  76. json2xml: function (o, tab) {
  77. var toXml = function (v, name, ind) {
  78. var xml = "", i, n, sXml, hasChild, m;
  79. if (v instanceof Array) {
  80. if (v.length === 0) {
  81. xml += ind + "<" + name + ">__EMPTY_ARRAY_</" + name + ">\n";
  82. } else {
  83. for (i = 0, n = v.length; i < n; i += 1) {
  84. sXml = ind + toXml(v[i], name, ind + "\t") + "\n";
  85. xml += sXml;
  86. }
  87. }
  88. } else if (typeof v === "object") {
  89. hasChild = false;
  90. xml += ind + "<" + name;
  91. for (m in v) {
  92. if (v.hasOwnProperty(m)) {
  93. if (m.charAt(0) === "@") {
  94. xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
  95. } else {
  96. hasChild = true;
  97. }
  98. }
  99. }
  100. xml += hasChild ? ">" : "/>";
  101. if (hasChild) {
  102. for (m in v) {
  103. if (v.hasOwnProperty(m)) {
  104. if (m === "#text") {
  105. xml += v[m];
  106. } else if (m === "#cdata") {
  107. xml += "<![CDATA[" + v[m] + "]]>";
  108. } else if (m.charAt(0) !== "@") {
  109. xml += toXml(v[m], m, ind + "\t");
  110. }
  111. }
  112. }
  113. xml += (xml.charAt(xml.length - 1) === "\n" ? ind : "") + "</" + name + ">";
  114. }
  115. } else if (typeof v === "function") {
  116. xml += ind + "<" + name + ">" + "<![CDATA[" + v + "]]>" + "</" + name + ">";
  117. } else {
  118. if (v === undefined) {
  119. v = "";
  120. }
  121. if (v.toString() === "\"\"" || v.toString().length === 0) {
  122. xml += ind + "<" + name + ">__EMPTY_STRING_</" + name + ">";
  123. } else {
  124. xml += ind + "<" + name + ">" + v.toString() + "</" + name + ">";
  125. }
  126. }
  127. return xml;
  128. },
  129. xml1 = "",
  130. p;
  131. for (p in o) {
  132. if (o.hasOwnProperty(p)) {
  133. xml1 += toXml(o[p], p, "");
  134. }
  135. }
  136. return tab ? xml1.replace(/\t/g, tab) : xml1.replace(/\t|\n/g, "");
  137. },
  138. // Internal methods
  139. toObj: function (xml) {
  140. var o = {}, funcTest = /function/i, i, textChild = 0, cdataChild = 0, hasElementChild = false, n;
  141. if (xml.nodeType === 1) {
  142. // element node ..
  143. if (xml.attributes.length) {
  144. // element with attributes ..
  145. for (i = 0; i < xml.attributes.length; i += 1) {
  146. o["@" + xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue || "").toString();
  147. }
  148. }
  149. if (xml.firstChild) {
  150. // element has child nodes ..
  151. for (n = xml.firstChild; n; n = n.nextSibling) {
  152. if (n.nodeType === 1) {
  153. hasElementChild = true;
  154. } else if (n.nodeType === 3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) {
  155. // non-whitespace text
  156. textChild += 1;
  157. } else if (n.nodeType === 4) {
  158. // cdata section node
  159. cdataChild += 1;
  160. }
  161. }
  162. if (hasElementChild) {
  163. if (textChild < 2 && cdataChild < 2) {
  164. // structured element with evtl. a single text or/and cdata node ..
  165. this.removeWhite(xml);
  166. for (n = xml.firstChild; n; n = n.nextSibling) {
  167. if (n.nodeType === 3) {
  168. // text node
  169. o["#text"] = this.escape(n.nodeValue);
  170. } else if (n.nodeType === 4) {
  171. // cdata node
  172. if (funcTest.test(n.nodeValue)) {
  173. o[n.nodeName] = [o[n.nodeName], n.nodeValue];
  174. } else {
  175. o["#cdata"] = this.escape(n.nodeValue);
  176. }
  177. } else if (o[n.nodeName]) {
  178. // multiple occurence of element ..
  179. if (o[n.nodeName] instanceof Array) {
  180. o[n.nodeName][o[n.nodeName].length] = this.toObj(n);
  181. } else {
  182. o[n.nodeName] = [o[n.nodeName], this.toObj(n)];
  183. }
  184. } else {
  185. // first occurence of element ..
  186. o[n.nodeName] = this.toObj(n);
  187. }
  188. }
  189. } else {
  190. // mixed content
  191. if (!xml.attributes.length) {
  192. o = this.escape(this.innerXml(xml));
  193. } else {
  194. o["#text"] = this.escape(this.innerXml(xml));
  195. }
  196. }
  197. } else if (textChild) {
  198. // pure text
  199. if (!xml.attributes.length) {
  200. o = this.escape(this.innerXml(xml));
  201. if (o === "__EMPTY_ARRAY_") {
  202. o = "[]";
  203. } else if (o === "__EMPTY_STRING_") {
  204. o = "";
  205. }
  206. } else {
  207. o["#text"] = this.escape(this.innerXml(xml));
  208. }
  209. } else if (cdataChild) {
  210. // cdata
  211. if (cdataChild > 1) {
  212. o = this.escape(this.innerXml(xml));
  213. } else {
  214. for (n = xml.firstChild; n; n = n.nextSibling) {
  215. if (funcTest.test(xml.firstChild.nodeValue)) {
  216. o = xml.firstChild.nodeValue;
  217. break;
  218. }
  219. o["#cdata"] = this.escape(n.nodeValue);
  220. }
  221. }
  222. }
  223. }
  224. if (!xml.attributes.length && !xml.firstChild) {
  225. o = null;
  226. }
  227. } else if (xml.nodeType === 9) {
  228. // document.node
  229. o = this.toObj(xml.documentElement);
  230. } else {
  231. ($.jgrid != null && $.jgrid.defaults != null && $.isFunction($.jgrid.defaults.fatalError) ? $.jgrid.defaults.fatalError : alert)("unhandled node type: " + xml.nodeType);
  232. }
  233. return o;
  234. },
  235. toJson: function (o, name, ind, wellform) {
  236. if (wellform === undefined) {
  237. wellform = true;
  238. }
  239. var json = name ? ("\"" + name + "\"") : "", tab = "\t", newline = "\n", n, i, ar = [], arr = [], m;
  240. if (!wellform) {
  241. tab = "";
  242. newline = "";
  243. }
  244. if (o === "[]") {
  245. json += (name ? ":[]" : "[]");
  246. } else if (o instanceof Array) {
  247. for (i = 0, n = o.length; i < n; i += 1) {
  248. ar[i] = this.toJson(o[i], "", ind + tab, wellform);
  249. }
  250. json += (name ? ":[" : "[") + (ar.length > 1 ? (newline + ind + tab + ar.join("," + newline + ind + tab) + newline + ind) : ar.join("")) + "]";
  251. } else if (o === null) {
  252. json += (name && ":") + "null";
  253. } else if (typeof o === "object") {
  254. for (m in o) {
  255. if (o.hasOwnProperty(m)) {
  256. arr[arr.length] = this.toJson(o[m], m, ind + tab, wellform);
  257. }
  258. }
  259. json += (name ? ":{" : "{") + (arr.length > 1 ? (newline + ind + tab + arr.join("," + newline + ind + tab) + newline + ind) : arr.join("")) + "}";
  260. } else if (typeof o === "string") {
  261. json += (name && ":") + "\"" + o.replace(/\\/g, "\\\\").replace(/\"/g, '\\"') + "\"";
  262. } else {
  263. json += (name && ":") + o.toString();
  264. }
  265. return json;
  266. },
  267. innerXml: function (node) {
  268. var s = "", child,
  269. asXml = function (n) {
  270. var str = "", i, c;
  271. if (n.nodeType === 1) {
  272. str += "<" + n.nodeName;
  273. for (i = 0; i < n.attributes.length; i += 1) {
  274. str += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\"";
  275. }
  276. if (n.firstChild) {
  277. str += ">";
  278. for (c = n.firstChild; c; c = c.nextSibling) {
  279. str += asXml(c);
  280. }
  281. str += "</" + n.nodeName + ">";
  282. } else {
  283. str += "/>";
  284. }
  285. } else if (n.nodeType === 3) {
  286. str += n.nodeValue;
  287. } else if (n.nodeType === 4) {
  288. str += "<![CDATA[" + n.nodeValue + "]]>";
  289. }
  290. return str;
  291. };
  292. if (node.hasOwnProperty("innerHTML")) {
  293. s = node.innerHTML;
  294. } else {
  295. for (child = node.firstChild; child; child = child.nextSibling) {
  296. s += asXml(child);
  297. }
  298. }
  299. return s;
  300. },
  301. escape: function (txt) {
  302. return txt.replace(/[\\]/g, "\\\\").replace(/[\"]/g, '\\"').replace(/[\n]/g, "\\n").replace(/[\r]/g, "\\r");
  303. },
  304. removeWhite: function (e) {
  305. e.normalize();
  306. var n = e.firstChild, nxt;
  307. while (n) {
  308. if (n.nodeType === 3) {
  309. // text node
  310. if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) {
  311. // pure whitespace text node
  312. nxt = n.nextSibling;
  313. e.removeChild(n);
  314. n = nxt;
  315. } else {
  316. n = n.nextSibling;
  317. }
  318. } else if (n.nodeType === 1) {
  319. // element node
  320. this.removeWhite(n);
  321. n = n.nextSibling;
  322. } else {
  323. // any other node
  324. n = n.nextSibling;
  325. }
  326. }
  327. return e;
  328. }
  329. };
  330. window.xmlJsonClass = xmlJsonClass;
  331. // end module jsonxml
  332. return xmlJsonClass;
  333. }));