browser_parser.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. var util = require('../util');
  2. var Shape = require('../model/shape');
  3. function DomXmlParser() { }
  4. DomXmlParser.prototype.parse = function(xml, shape) {
  5. if (xml.replace(/^\s+/, '') === '') return {};
  6. var result, error;
  7. try {
  8. if (window.DOMParser) {
  9. try {
  10. var parser = new DOMParser();
  11. result = parser.parseFromString(xml, 'text/xml');
  12. } catch (syntaxError) {
  13. throw util.error(new Error('Parse error in document'),
  14. {
  15. originalError: syntaxError,
  16. code: 'XMLParserError',
  17. retryable: true
  18. });
  19. }
  20. if (result.documentElement === null) {
  21. throw util.error(new Error('Cannot parse empty document.'),
  22. {
  23. code: 'XMLParserError',
  24. retryable: true
  25. });
  26. }
  27. var isError = result.getElementsByTagName('parsererror')[0];
  28. if (isError && (isError.parentNode === result ||
  29. isError.parentNode.nodeName === 'body' ||
  30. isError.parentNode.parentNode === result ||
  31. isError.parentNode.parentNode.nodeName === 'body')) {
  32. var errorElement = isError.getElementsByTagName('div')[0] || isError;
  33. throw util.error(new Error(errorElement.textContent || 'Parser error in document'),
  34. {
  35. code: 'XMLParserError',
  36. retryable: true
  37. });
  38. }
  39. } else if (window.ActiveXObject) {
  40. result = new window.ActiveXObject('Microsoft.XMLDOM');
  41. result.async = false;
  42. if (!result.loadXML(xml)) {
  43. throw util.error(new Error('Parse error in document'),
  44. {
  45. code: 'XMLParserError',
  46. retryable: true
  47. });
  48. }
  49. } else {
  50. throw new Error('Cannot load XML parser');
  51. }
  52. } catch (e) {
  53. error = e;
  54. }
  55. if (result && result.documentElement && !error) {
  56. var data = parseXml(result.documentElement, shape);
  57. var metadata = getElementByTagName(result.documentElement, 'ResponseMetadata');
  58. if (metadata) {
  59. data.ResponseMetadata = parseXml(metadata, {});
  60. }
  61. return data;
  62. } else if (error) {
  63. throw util.error(error || new Error(), {code: 'XMLParserError', retryable: true});
  64. } else { // empty xml document
  65. return {};
  66. }
  67. };
  68. function getElementByTagName(xml, tag) {
  69. var elements = xml.getElementsByTagName(tag);
  70. for (var i = 0, iLen = elements.length; i < iLen; i++) {
  71. if (elements[i].parentNode === xml) {
  72. return elements[i];
  73. }
  74. }
  75. }
  76. function parseXml(xml, shape) {
  77. if (!shape) shape = {};
  78. switch (shape.type) {
  79. case 'structure': return parseStructure(xml, shape);
  80. case 'map': return parseMap(xml, shape);
  81. case 'list': return parseList(xml, shape);
  82. case undefined: case null: return parseUnknown(xml);
  83. default: return parseScalar(xml, shape);
  84. }
  85. }
  86. function parseStructure(xml, shape) {
  87. var data = {};
  88. if (xml === null) return data;
  89. util.each(shape.members, function(memberName, memberShape) {
  90. if (memberShape.isXmlAttribute) {
  91. if (Object.prototype.hasOwnProperty.call(xml.attributes, memberShape.name)) {
  92. var value = xml.attributes[memberShape.name].value;
  93. data[memberName] = parseXml({textContent: value}, memberShape);
  94. }
  95. } else {
  96. var xmlChild = memberShape.flattened ? xml :
  97. getElementByTagName(xml, memberShape.name);
  98. if (xmlChild) {
  99. data[memberName] = parseXml(xmlChild, memberShape);
  100. } else if (
  101. !memberShape.flattened &&
  102. memberShape.type === 'list' &&
  103. !shape.api.xmlNoDefaultLists) {
  104. data[memberName] = memberShape.defaultValue;
  105. }
  106. }
  107. });
  108. return data;
  109. }
  110. function parseMap(xml, shape) {
  111. var data = {};
  112. var xmlKey = shape.key.name || 'key';
  113. var xmlValue = shape.value.name || 'value';
  114. var tagName = shape.flattened ? shape.name : 'entry';
  115. var child = xml.firstElementChild;
  116. while (child) {
  117. if (child.nodeName === tagName) {
  118. var key = getElementByTagName(child, xmlKey).textContent;
  119. var value = getElementByTagName(child, xmlValue);
  120. data[key] = parseXml(value, shape.value);
  121. }
  122. child = child.nextElementSibling;
  123. }
  124. return data;
  125. }
  126. function parseList(xml, shape) {
  127. var data = [];
  128. var tagName = shape.flattened ? shape.name : (shape.member.name || 'member');
  129. var child = xml.firstElementChild;
  130. while (child) {
  131. if (child.nodeName === tagName) {
  132. data.push(parseXml(child, shape.member));
  133. }
  134. child = child.nextElementSibling;
  135. }
  136. return data;
  137. }
  138. function parseScalar(xml, shape) {
  139. if (xml.getAttribute) {
  140. var encoding = xml.getAttribute('encoding');
  141. if (encoding === 'base64') {
  142. shape = new Shape.create({type: encoding});
  143. }
  144. }
  145. var text = xml.textContent;
  146. if (text === '') text = null;
  147. if (typeof shape.toType === 'function') {
  148. return shape.toType(text);
  149. } else {
  150. return text;
  151. }
  152. }
  153. function parseUnknown(xml) {
  154. if (xml === undefined || xml === null) return '';
  155. // empty object
  156. if (!xml.firstElementChild) {
  157. if (xml.parentNode.parentNode === null) return {};
  158. if (xml.childNodes.length === 0) return '';
  159. else return xml.textContent;
  160. }
  161. // object, parse as structure
  162. var shape = {type: 'structure', members: {}};
  163. var child = xml.firstElementChild;
  164. while (child) {
  165. var tag = child.nodeName;
  166. if (Object.prototype.hasOwnProperty.call(shape.members, tag)) {
  167. // multiple tags of the same name makes it a list
  168. shape.members[tag].type = 'list';
  169. } else {
  170. shape.members[tag] = {name: tag};
  171. }
  172. child = child.nextElementSibling;
  173. }
  174. return parseStructure(xml, shape);
  175. }
  176. /**
  177. * @api private
  178. */
  179. module.exports = DomXmlParser;