node_parser.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. var AWS = require('../core');
  2. var util = AWS.util;
  3. var Shape = AWS.Model.Shape;
  4. var xml2js = require('xml2js');
  5. /**
  6. * @api private
  7. */
  8. var options = { // options passed to xml2js parser
  9. explicitCharkey: false, // undocumented
  10. trim: false, // trim the leading/trailing whitespace from text nodes
  11. normalize: false, // trim interior whitespace inside text nodes
  12. explicitRoot: false, // return the root node in the resulting object?
  13. emptyTag: null, // the default value for empty nodes
  14. explicitArray: true, // always put child nodes in an array
  15. ignoreAttrs: false, // ignore attributes, only create text nodes
  16. mergeAttrs: false, // merge attributes and child elements
  17. validator: null // a callable validator
  18. };
  19. function NodeXmlParser() { }
  20. NodeXmlParser.prototype.parse = function(xml, shape) {
  21. shape = shape || {};
  22. var result = null;
  23. var error = null;
  24. var parser = new xml2js.Parser(options);
  25. parser.parseString(xml, function (e, r) {
  26. error = e;
  27. result = r;
  28. });
  29. if (result) {
  30. var data = parseXml(result, shape);
  31. if (result.ResponseMetadata) {
  32. data.ResponseMetadata = parseXml(result.ResponseMetadata[0], {});
  33. }
  34. return data;
  35. } else if (error) {
  36. throw util.error(error, {code: 'XMLParserError', retryable: true});
  37. } else { // empty xml document
  38. return parseXml({}, shape);
  39. }
  40. };
  41. function parseXml(xml, shape) {
  42. switch (shape.type) {
  43. case 'structure': return parseStructure(xml, shape);
  44. case 'map': return parseMap(xml, shape);
  45. case 'list': return parseList(xml, shape);
  46. case undefined: case null: return parseUnknown(xml);
  47. default: return parseScalar(xml, shape);
  48. }
  49. }
  50. function parseStructure(xml, shape) {
  51. var data = {};
  52. if (xml === null) return data;
  53. util.each(shape.members, function(memberName, memberShape) {
  54. var xmlName = memberShape.name;
  55. if (Object.prototype.hasOwnProperty.call(xml, xmlName) && Array.isArray(xml[xmlName])) {
  56. var xmlChild = xml[xmlName];
  57. if (!memberShape.flattened) xmlChild = xmlChild[0];
  58. data[memberName] = parseXml(xmlChild, memberShape);
  59. } else if (memberShape.isXmlAttribute &&
  60. xml.$ && Object.prototype.hasOwnProperty.call(xml.$, xmlName)) {
  61. data[memberName] = parseScalar(xml.$[xmlName], memberShape);
  62. } else if (memberShape.type === 'list' && !shape.api.xmlNoDefaultLists) {
  63. data[memberName] = memberShape.defaultValue;
  64. }
  65. });
  66. return data;
  67. }
  68. function parseMap(xml, shape) {
  69. var data = {};
  70. if (xml === null) return data;
  71. var xmlKey = shape.key.name || 'key';
  72. var xmlValue = shape.value.name || 'value';
  73. var iterable = shape.flattened ? xml : xml.entry;
  74. if (Array.isArray(iterable)) {
  75. util.arrayEach(iterable, function(child) {
  76. data[child[xmlKey][0]] = parseXml(child[xmlValue][0], shape.value);
  77. });
  78. }
  79. return data;
  80. }
  81. function parseList(xml, shape) {
  82. var data = [];
  83. var name = shape.member.name || 'member';
  84. if (shape.flattened) {
  85. util.arrayEach(xml, function(xmlChild) {
  86. data.push(parseXml(xmlChild, shape.member));
  87. });
  88. } else if (xml && Array.isArray(xml[name])) {
  89. util.arrayEach(xml[name], function(child) {
  90. data.push(parseXml(child, shape.member));
  91. });
  92. }
  93. return data;
  94. }
  95. function parseScalar(text, shape) {
  96. if (text && text.$ && text.$.encoding === 'base64') {
  97. shape = new Shape.create({type: text.$.encoding});
  98. }
  99. if (text && text._) text = text._;
  100. if (typeof shape.toType === 'function') {
  101. return shape.toType(text);
  102. } else {
  103. return text;
  104. }
  105. }
  106. function parseUnknown(xml) {
  107. if (xml === undefined || xml === null) return '';
  108. if (typeof xml === 'string') return xml;
  109. // parse a list
  110. if (Array.isArray(xml)) {
  111. var arr = [];
  112. for (i = 0; i < xml.length; i++) {
  113. arr.push(parseXml(xml[i], {}));
  114. }
  115. return arr;
  116. }
  117. // empty object
  118. var keys = Object.keys(xml), i;
  119. if (keys.length === 0 || (keys.length === 1 && keys[0] === '$')) {
  120. return {};
  121. }
  122. // object, parse as structure
  123. var data = {};
  124. for (i = 0; i < keys.length; i++) {
  125. var key = keys[i], value = xml[key];
  126. if (key === '$') continue;
  127. if (value.length > 1) { // this member is a list
  128. data[key] = parseList(value, {member: {}});
  129. } else { // this member is a single item
  130. data[key] = parseXml(value[0], {});
  131. }
  132. }
  133. return data;
  134. }
  135. /**
  136. * @api private
  137. */
  138. module.exports = NodeXmlParser;