jstoxml.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. var toXML = function(obj, config){
  2. // include XML header
  3. config = config || {};
  4. var out = '';
  5. if(config.header) {
  6. if(typeof config.header == 'string') {
  7. out = config.header;
  8. } else {
  9. out = '<?xml version="1.0" encoding="UTF-8"?>\n';
  10. }
  11. }
  12. var origIndent = config.indent || '';
  13. var indent = '';
  14. var filter = function customFilter(txt) {
  15. if(!config.filter) return txt;
  16. var mappings = config.filter;
  17. var replacements = [];
  18. for(var map in mappings) {
  19. if(!mappings.hasOwnProperty(map)) continue;
  20. replacements.push(map);
  21. }
  22. return String(txt).replace(new RegExp('(' + replacements.join('|') + ')', 'g'), function(str, entity) {
  23. return mappings[entity] || '';
  24. });
  25. };
  26. // helper function to push a new line to the output
  27. var push = function(string){
  28. out += string + (origIndent ? '\n' : '');
  29. };
  30. /* create a tag and add it to the output
  31. Example:
  32. outputTag({
  33. name: 'myTag', // creates a tag <myTag>
  34. indent: ' ', // indent string to prepend
  35. closeTag: true, // starts and closes a tag on the same line
  36. selfCloseTag: true,
  37. attrs: { // attributes
  38. foo: 'bar', // results in <myTag foo="bar">
  39. foo2: 'bar2'
  40. }
  41. });
  42. */
  43. var outputTag = function(tag){
  44. var attrsString = '';
  45. var outputString = '';
  46. var attrs = tag.attrs || '';
  47. // turn the attributes object into a string with key="value" pairs
  48. for(var attr in attrs){
  49. if(attrs.hasOwnProperty(attr)) {
  50. attrsString += ' ' + attr + '="' + attrs[attr] + '"';
  51. }
  52. }
  53. // assemble the tag
  54. outputString += (tag.indent || '') + '<' + (tag.closeTag ? '/' : '') + tag.name + (!tag.closeTag ? attrsString : '') + (tag.selfCloseTag ? '/' : '') + '>';
  55. // if the tag only contains a text string, output it and close the tag
  56. if(tag.text || tag.text === ''){
  57. outputString += filter(tag.text) + '</' + tag.name + '>';
  58. }
  59. push(outputString);
  60. };
  61. // custom-tailored iterator for input arrays/objects (NOT a general purpose iterator)
  62. var every = function(obj, fn, indent){
  63. // array
  64. if(Array.isArray(obj)){
  65. obj.every(function(elt){ // for each element in the array
  66. fn(elt, indent);
  67. return true; // continue to iterate
  68. });
  69. return;
  70. }
  71. // object with tag name
  72. if(obj._name){
  73. fn(obj, indent);
  74. return;
  75. }
  76. // iterable object
  77. for(var key in obj){
  78. var type = typeof obj[key];
  79. if(obj.hasOwnProperty(key) && (obj[key] || type === 'boolean' || type === 'number')){
  80. fn({_name: key, _content: obj[key]}, indent);
  81. //} else if(!obj[key]) { // null value (foo:'')
  82. } else if(obj.hasOwnProperty(key) && obj[key] === null) { // null value (foo:null)
  83. fn(key, indent); // output the keyname as a string ('foo')
  84. } else if(obj.hasOwnProperty(key) && obj[key] === '') {
  85. // blank string
  86. outputTag({
  87. name: key,
  88. text: ''
  89. });
  90. }
  91. }
  92. };
  93. var convert = function convert(input, indent){
  94. var type = typeof input;
  95. if(!indent) indent = '';
  96. if(Array.isArray(input)) type = 'array';
  97. var path = {
  98. 'string': function(){
  99. push(indent + filter(input));
  100. },
  101. 'boolean': function(){
  102. push(indent + (input ? 'true' : 'false'));
  103. },
  104. 'number': function(){
  105. push(indent + input);
  106. },
  107. 'array': function(){
  108. every(input, convert, indent);
  109. },
  110. 'function': function(){
  111. push(indent + input());
  112. },
  113. 'object': function(){
  114. if(!input._name){
  115. every(input, convert, indent);
  116. return;
  117. }
  118. var outputTagObj = {
  119. name: input._name,
  120. indent: indent,
  121. attrs: input._attrs
  122. };
  123. var type = typeof input._content;
  124. if(type === 'undefined' || input._content._selfCloseTag === true){
  125. if (input._content && input._content._attrs) {
  126. outputTagObj.attrs = input._content._attrs;
  127. }
  128. outputTagObj.selfCloseTag = true;
  129. outputTag(outputTagObj);
  130. return;
  131. }
  132. var objContents = {
  133. 'string': function(){
  134. outputTagObj.text = input._content;
  135. outputTag(outputTagObj);
  136. },
  137. 'boolean': function(){
  138. outputTagObj.text = (input._content ? 'true' : 'false');
  139. outputTag(outputTagObj);
  140. },
  141. 'number': function(){
  142. outputTagObj.text = input._content.toString();
  143. outputTag(outputTagObj);
  144. },
  145. 'object': function(){ // or Array
  146. outputTag(outputTagObj);
  147. every(input._content, convert, indent + origIndent);
  148. outputTagObj.closeTag = true;
  149. outputTag(outputTagObj);
  150. },
  151. 'function': function(){
  152. outputTagObj.text = input._content(); // () to execute the fn
  153. outputTag(outputTagObj);
  154. }
  155. };
  156. if(objContents[type]) objContents[type]();
  157. }
  158. };
  159. if(path[type]) path[type]();
  160. };
  161. convert(obj, indent);
  162. return out;
  163. };
  164. exports.toXML = toXML;