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