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