123 lines
2.6 KiB
JavaScript
123 lines
2.6 KiB
JavaScript
|
//////////////////////////////////////////
|
||
|
// Defines mappings between content-type
|
||
|
// and the appropriate parsers.
|
||
|
//////////////////////////////////////////
|
||
|
|
||
|
var Transform = require('stream').Transform;
|
||
|
var sax = require('sax');
|
||
|
|
||
|
function parseXML(str, cb) {
|
||
|
var obj, current, parser = sax.parser(true, { trim: true, lowercase: true })
|
||
|
parser.onerror = parser.onend = done;
|
||
|
|
||
|
function done(err) {
|
||
|
parser.onerror = parser.onend = function() { }
|
||
|
cb(err, obj)
|
||
|
}
|
||
|
|
||
|
function newElement(name, attributes) {
|
||
|
return {
|
||
|
name: name || '',
|
||
|
value: '',
|
||
|
attributes: attributes || {},
|
||
|
children: []
|
||
|
}
|
||
|
}
|
||
|
|
||
|
parser.oncdata = parser.ontext = function(t) {
|
||
|
if (current) current.value += t
|
||
|
}
|
||
|
|
||
|
parser.onopentag = function(node) {
|
||
|
var element = newElement(node.name, node.attributes)
|
||
|
if (current) {
|
||
|
element.parent = current
|
||
|
current.children.push(element)
|
||
|
} else { // root object
|
||
|
obj = element
|
||
|
}
|
||
|
|
||
|
current = element
|
||
|
};
|
||
|
|
||
|
parser.onclosetag = function() {
|
||
|
if (typeof current.parent !== 'undefined') {
|
||
|
var just_closed = current
|
||
|
current = current.parent
|
||
|
delete just_closed.parent
|
||
|
}
|
||
|
}
|
||
|
|
||
|
parser.write(str).close()
|
||
|
}
|
||
|
|
||
|
function parserFactory(name, fn) {
|
||
|
|
||
|
function parser() {
|
||
|
var chunks = [],
|
||
|
stream = new Transform({ objectMode: true });
|
||
|
|
||
|
// Buffer all our data
|
||
|
stream._transform = function(chunk, encoding, done) {
|
||
|
chunks.push(chunk);
|
||
|
done();
|
||
|
}
|
||
|
|
||
|
// And call the parser when all is there.
|
||
|
stream._flush = function(done) {
|
||
|
var self = this,
|
||
|
data = Buffer.concat(chunks);
|
||
|
|
||
|
try {
|
||
|
fn(data, function(err, result) {
|
||
|
if (err) throw err;
|
||
|
self.push(result);
|
||
|
});
|
||
|
} catch (err) {
|
||
|
self.push(data); // just pass the original data
|
||
|
} finally {
|
||
|
done();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return stream;
|
||
|
}
|
||
|
|
||
|
return { fn: parser, name: name };
|
||
|
}
|
||
|
|
||
|
var parsers = {}
|
||
|
|
||
|
function buildParser(name, types, fn) {
|
||
|
var parser = parserFactory(name, fn);
|
||
|
types.forEach(function(type) {
|
||
|
parsers[type] = parser;
|
||
|
})
|
||
|
}
|
||
|
|
||
|
buildParser('json', [
|
||
|
'application/json',
|
||
|
'application/hal+json',
|
||
|
'text/javascript',
|
||
|
'application/vnd.api+json'
|
||
|
], function(buffer, cb) {
|
||
|
var err, data;
|
||
|
try { data = JSON.parse(buffer); } catch (e) { err = e; }
|
||
|
cb(err, data);
|
||
|
});
|
||
|
|
||
|
buildParser('xml', [
|
||
|
'text/xml',
|
||
|
'application/xml',
|
||
|
'application/rdf+xml',
|
||
|
'application/rss+xml',
|
||
|
'application/atom+xml'
|
||
|
], function(buffer, cb) {
|
||
|
parseXML(buffer.toString(), function(err, obj) {
|
||
|
cb(err, obj)
|
||
|
})
|
||
|
});
|
||
|
|
||
|
module.exports = parsers;
|
||
|
module.exports.use = buildParser;
|