json2xml.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. var sanitizer = require('./sanitize.js')
  2. module.exports = function (json, options) {
  3. if (json instanceof Buffer) {
  4. json = json.toString();
  5. }
  6. var obj = null;
  7. if (typeof(json) == 'string') {
  8. try {
  9. obj = JSON.parse(json);
  10. } catch(e) {
  11. throw new Error("The JSON structure is invalid");
  12. }
  13. } else {
  14. obj = json;
  15. }
  16. var toXml = new ToXml(options);
  17. toXml.parse(obj);
  18. return toXml.xml;
  19. }
  20. ToXml.prototype.parse = function(obj) {
  21. var self = this;
  22. var keys = Object.keys(obj);
  23. var len = keys.length;
  24. // First pass, extract strings only
  25. for (var i = 0; i < len; i++) {
  26. var key = keys[i], value = obj[key], isArray = Array.isArray(value);
  27. var type = typeof(value);
  28. if (type == 'string' || type == 'number' || type == 'boolean' || isArray) {
  29. var it = isArray ? value : [value];
  30. it.forEach(function(subVal) {
  31. if (typeof(subVal) != 'object') {
  32. if (key == '$t') {
  33. self.addTextContent(subVal);
  34. } else {
  35. self.addAttr(key, subVal);
  36. }
  37. }
  38. });
  39. }
  40. }
  41. // Second path, now handle sub-objects and arrays
  42. for (var i = 0; i < len; i++) {
  43. var key = keys[i];
  44. if (Array.isArray(obj[key])) {
  45. var elems = obj[key];
  46. var l = elems.length;
  47. for (var j = 0; j < l; j++) {
  48. var elem = elems[j];
  49. if (typeof(elem) == 'object') {
  50. self.openTag(key);
  51. self.parse(elem);
  52. self.closeTag(key);
  53. }
  54. }
  55. } else if (typeof(obj[key]) == 'object') {
  56. self.openTag(key);
  57. self.parse(obj[key]);
  58. self.closeTag(key);
  59. }
  60. }
  61. };
  62. ToXml.prototype.openTag = function(key) {
  63. this.completeTag();
  64. this.xml += '<' + key;
  65. this.tagIncomplete = true;
  66. }
  67. ToXml.prototype.addAttr = function(key, val) {
  68. if (this.options.sanitize) {
  69. val = sanitizer.sanitize(val)
  70. }
  71. this.xml += ' ' + key + '="' + val + '"';
  72. }
  73. ToXml.prototype.addTextContent = function(text) {
  74. this.completeTag();
  75. this.xml += text;
  76. }
  77. ToXml.prototype.closeTag = function(key) {
  78. this.completeTag();
  79. this.xml += '</' + key + '>';
  80. }
  81. ToXml.prototype.completeTag = function() {
  82. if (this.tagIncomplete) {
  83. this.xml += '>';
  84. this.tagIncomplete = false;
  85. }
  86. }
  87. function ToXml(options) {
  88. var defaultOpts = {
  89. sanitize: false
  90. };
  91. if (options) {
  92. for (var opt in options) {
  93. defaultOpts[opt] = options[opt];
  94. }
  95. }
  96. this.options = defaultOpts;
  97. this.xml = '';
  98. this.tagIncomplete = false;
  99. }