199 lines
5.5 KiB
JavaScript
199 lines
5.5 KiB
JavaScript
var util = require('../util');
|
|
var Shape = require('../model/shape');
|
|
|
|
function DomXmlParser() { }
|
|
|
|
DomXmlParser.prototype.parse = function(xml, shape) {
|
|
if (xml.replace(/^\s+/, '') === '') return {};
|
|
|
|
var result, error;
|
|
try {
|
|
if (window.DOMParser) {
|
|
try {
|
|
var parser = new DOMParser();
|
|
result = parser.parseFromString(xml, 'text/xml');
|
|
} catch (syntaxError) {
|
|
throw util.error(new Error('Parse error in document'),
|
|
{
|
|
originalError: syntaxError,
|
|
code: 'XMLParserError',
|
|
retryable: true
|
|
});
|
|
}
|
|
|
|
if (result.documentElement === null) {
|
|
throw util.error(new Error('Cannot parse empty document.'),
|
|
{
|
|
code: 'XMLParserError',
|
|
retryable: true
|
|
});
|
|
}
|
|
|
|
var isError = result.getElementsByTagName('parsererror')[0];
|
|
if (isError && (isError.parentNode === result ||
|
|
isError.parentNode.nodeName === 'body' ||
|
|
isError.parentNode.parentNode === result ||
|
|
isError.parentNode.parentNode.nodeName === 'body')) {
|
|
var errorElement = isError.getElementsByTagName('div')[0] || isError;
|
|
throw util.error(new Error(errorElement.textContent || 'Parser error in document'),
|
|
{
|
|
code: 'XMLParserError',
|
|
retryable: true
|
|
});
|
|
}
|
|
} else if (window.ActiveXObject) {
|
|
result = new window.ActiveXObject('Microsoft.XMLDOM');
|
|
result.async = false;
|
|
|
|
if (!result.loadXML(xml)) {
|
|
throw util.error(new Error('Parse error in document'),
|
|
{
|
|
code: 'XMLParserError',
|
|
retryable: true
|
|
});
|
|
}
|
|
} else {
|
|
throw new Error('Cannot load XML parser');
|
|
}
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
|
|
if (result && result.documentElement && !error) {
|
|
var data = parseXml(result.documentElement, shape);
|
|
var metadata = getElementByTagName(result.documentElement, 'ResponseMetadata');
|
|
if (metadata) {
|
|
data.ResponseMetadata = parseXml(metadata, {});
|
|
}
|
|
return data;
|
|
} else if (error) {
|
|
throw util.error(error || new Error(), {code: 'XMLParserError', retryable: true});
|
|
} else { // empty xml document
|
|
return {};
|
|
}
|
|
};
|
|
|
|
function getElementByTagName(xml, tag) {
|
|
var elements = xml.getElementsByTagName(tag);
|
|
for (var i = 0, iLen = elements.length; i < iLen; i++) {
|
|
if (elements[i].parentNode === xml) {
|
|
return elements[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
function parseXml(xml, shape) {
|
|
if (!shape) 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) {
|
|
if (memberShape.isXmlAttribute) {
|
|
if (Object.prototype.hasOwnProperty.call(xml.attributes, memberShape.name)) {
|
|
var value = xml.attributes[memberShape.name].value;
|
|
data[memberName] = parseXml({textContent: value}, memberShape);
|
|
}
|
|
} else {
|
|
var xmlChild = memberShape.flattened ? xml :
|
|
getElementByTagName(xml, memberShape.name);
|
|
if (xmlChild) {
|
|
data[memberName] = parseXml(xmlChild, memberShape);
|
|
} else if (!memberShape.flattened && memberShape.type === 'list') {
|
|
data[memberName] = memberShape.defaultValue;
|
|
}
|
|
}
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
function parseMap(xml, shape) {
|
|
var data = {};
|
|
var xmlKey = shape.key.name || 'key';
|
|
var xmlValue = shape.value.name || 'value';
|
|
var tagName = shape.flattened ? shape.name : 'entry';
|
|
|
|
var child = xml.firstElementChild;
|
|
while (child) {
|
|
if (child.nodeName === tagName) {
|
|
var key = getElementByTagName(child, xmlKey).textContent;
|
|
var value = getElementByTagName(child, xmlValue);
|
|
data[key] = parseXml(value, shape.value);
|
|
}
|
|
child = child.nextElementSibling;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
function parseList(xml, shape) {
|
|
var data = [];
|
|
var tagName = shape.flattened ? shape.name : (shape.member.name || 'member');
|
|
|
|
var child = xml.firstElementChild;
|
|
while (child) {
|
|
if (child.nodeName === tagName) {
|
|
data.push(parseXml(child, shape.member));
|
|
}
|
|
child = child.nextElementSibling;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
function parseScalar(xml, shape) {
|
|
if (xml.getAttribute) {
|
|
var encoding = xml.getAttribute('encoding');
|
|
if (encoding === 'base64') {
|
|
shape = new Shape.create({type: encoding});
|
|
}
|
|
}
|
|
|
|
var text = xml.textContent;
|
|
if (text === '') text = null;
|
|
if (typeof shape.toType === 'function') {
|
|
return shape.toType(text);
|
|
} else {
|
|
return text;
|
|
}
|
|
}
|
|
|
|
function parseUnknown(xml) {
|
|
if (xml === undefined || xml === null) return '';
|
|
|
|
// empty object
|
|
if (!xml.firstElementChild) {
|
|
if (xml.parentNode.parentNode === null) return {};
|
|
if (xml.childNodes.length === 0) return '';
|
|
else return xml.textContent;
|
|
}
|
|
|
|
// object, parse as structure
|
|
var shape = {type: 'structure', members: {}};
|
|
var child = xml.firstElementChild;
|
|
while (child) {
|
|
var tag = child.nodeName;
|
|
if (Object.prototype.hasOwnProperty.call(shape.members, tag)) {
|
|
// multiple tags of the same name makes it a list
|
|
shape.members[tag].type = 'list';
|
|
} else {
|
|
shape.members[tag] = {name: tag};
|
|
}
|
|
child = child.nextElementSibling;
|
|
}
|
|
return parseStructure(xml, shape);
|
|
}
|
|
|
|
/**
|
|
* @api private
|
|
*/
|
|
module.exports = DomXmlParser;
|