add stream.Transform API implementation
This commit is contained in:
60
parser.js
60
parser.js
@@ -1,34 +1,43 @@
|
||||
var expat = require('node-expat')
|
||||
var _ = require('lodash')
|
||||
var util = require('util')
|
||||
var stream = require('stream')
|
||||
|
||||
var ParserState = require('./parserState')
|
||||
|
||||
function XmlParser (xmlStream, opts) {
|
||||
function XmlParser (opts) {
|
||||
this.opts = opts || {}
|
||||
this.parserState = new ParserState()
|
||||
this.parser = new expat.Parser('UTF-8')
|
||||
var scope = this
|
||||
this.parser.pause = function () {
|
||||
xmlStream.pause()
|
||||
scope.parser.stop()
|
||||
var transformOpts = { readableObjectMode: true }
|
||||
stream.Transform.call(this, transformOpts)
|
||||
}
|
||||
this.parser.restart = function () {
|
||||
scope.parser.resume()
|
||||
xmlStream.resume()
|
||||
}
|
||||
process.nextTick(function () {
|
||||
parse.call(scope, xmlStream)
|
||||
})
|
||||
return this.parser
|
||||
util.inherits(XmlParser, stream.Transform)
|
||||
|
||||
XmlParser.prototype._transform = function (chunk, encoding, callback) {
|
||||
if (!this.opts.resourcePath) this.emit('error', new Error('resourcePath missing'))
|
||||
if (encoding !== 'buffer') this.emit('error', new Error('unsupported encoding'))
|
||||
|
||||
this.parse(chunk)
|
||||
callback()
|
||||
}
|
||||
|
||||
function parse (xmlStream) {
|
||||
if (!this.opts.resourcePath) this.parser.emit('error', new Error('resourcePath missing'))
|
||||
XmlParser.prototype.parse = function (chunk) {
|
||||
var scope = this
|
||||
var parser = scope.parser
|
||||
var parser = this.parser
|
||||
var state = this.parserState
|
||||
var lastIndex
|
||||
var resourcePath = this.opts.resourcePath
|
||||
|
||||
if (state.isRootNode) registerEvents()
|
||||
|
||||
if (typeof chunk === 'string') {
|
||||
parser.parse('', true)
|
||||
} else {
|
||||
parser.parse(chunk.toString())
|
||||
}
|
||||
|
||||
function registerEvents () {
|
||||
parser.on('startElement', function (name, attrs) {
|
||||
if (state.isRootNode) validateResourcePath(name)
|
||||
state.currentPath = state.currentPath + '/' + name
|
||||
@@ -48,10 +57,14 @@ function parse (xmlStream) {
|
||||
if (state.isPathfound) processText(text)
|
||||
})
|
||||
|
||||
parser.on('end', function () {
|
||||
parser.emit('finish')
|
||||
parser.on('error', function (err) {
|
||||
scope.emit('error', new Error(err + 'at line no:' + parser.getCurrentLineNumber() + ' on column no:' + parser.getCurrentColumnNumber()))
|
||||
})
|
||||
|
||||
parser.on('end', function () {
|
||||
})
|
||||
}
|
||||
|
||||
function processStartElement (name, attrs) {
|
||||
if (!name) return
|
||||
var obj = {}
|
||||
@@ -81,8 +94,8 @@ function parse (xmlStream) {
|
||||
var rpath = resourcePath.substring(0, index)
|
||||
|
||||
if (rpath === state.currentPath) {
|
||||
if (scope.opts.emitEventsOnNodeName) parser.emit(name, state.object)
|
||||
parser.emit('data', state.object)
|
||||
if (scope.opts.emitEventsOnNodeName) scope.emit(name, state.object)
|
||||
scope.push(state.object)
|
||||
state.object = {}
|
||||
}
|
||||
}
|
||||
@@ -146,10 +159,15 @@ function parse (xmlStream) {
|
||||
temp = temp.substring(0, index)
|
||||
|
||||
if (temp !== name) {
|
||||
xmlStream.end()
|
||||
this.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XmlParser.prototype._flush = function (callback) {
|
||||
this.parse('')
|
||||
callback()
|
||||
}
|
||||
|
||||
module.exports = XmlParser
|
||||
|
||||
|
||||
8347
test/TestFiles/hugeFile.xml
Normal file
8347
test/TestFiles/hugeFile.xml
Normal file
File diff suppressed because it is too large
Load Diff
51
test/test.js
51
test/test.js
@@ -7,7 +7,7 @@ describe('Tests', function () {
|
||||
describe('simple behaviour testing', function () {
|
||||
it('should properly parse a simple file.', function (done) {
|
||||
var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
|
||||
var parser = new ParserFactory(xmlStream, {resourcePath: '/items/item'})
|
||||
var parser = new ParserFactory({resourcePath: '/items/item'})
|
||||
var expectedData = [
|
||||
{ '$': { id: '1', test: 'hello' },
|
||||
subitem:
|
||||
@@ -39,7 +39,7 @@ describe('Tests', function () {
|
||||
|
||||
it('should properly parse a medium size file.', function (done) {
|
||||
var xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
|
||||
var parser = new ParserFactory(xmlStream, {resourcePath: '/items/item'})
|
||||
var parser = new ParserFactory({resourcePath: '/items/item'})
|
||||
|
||||
var dataEventCount = 0
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('Tests', function () {
|
||||
|
||||
it('should properly parse a file containing many nodes.', function (done) {
|
||||
var xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
|
||||
var parser = new ParserFactory(xmlStream, {resourcePath: '/items/item'})
|
||||
var parser = new ParserFactory({resourcePath: '/items/item'})
|
||||
|
||||
var dataEventCount = 0
|
||||
|
||||
@@ -83,7 +83,7 @@ describe('Tests', function () {
|
||||
|
||||
it('should properly parse a xml simple file in which nodes contain text values randomly.', function (done) {
|
||||
var xmlStream = fs.createReadStream('./test/TestFiles/randomText.xml')
|
||||
var parser = new ParserFactory(xmlStream, {resourcePath: '/items/item'})
|
||||
var parser = new ParserFactory({resourcePath: '/items/item'})
|
||||
var expectedData = [ { '$': { 'id': '1', 'test': 'hello' }, '_': ' item one two',
|
||||
'subitem': [ { '$': { 'sub': 'TESTING SUB' }, '_': 'one' },
|
||||
{ '$': { 'sub': '2' }, '_': 'two' } ] },
|
||||
@@ -111,5 +111,48 @@ describe('Tests', function () {
|
||||
})
|
||||
xmlStream.pipe(parser)
|
||||
})
|
||||
|
||||
it('should properly parse a file containing many nodes.', function (done) {
|
||||
var xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
|
||||
var parser = new ParserFactory({resourcePath: '/items/item'})
|
||||
// console.log(parser)
|
||||
var dataEventCount = 0
|
||||
|
||||
parser.on('data', function (data) {
|
||||
dataEventCount++
|
||||
})
|
||||
|
||||
parser.on('error', function (err) {
|
||||
done(err)
|
||||
})
|
||||
|
||||
parser.on('end', function () {
|
||||
// console.log('dataEventCount=', dataEventCount)
|
||||
dataEventCount.should.equal(296)
|
||||
done()
|
||||
})
|
||||
xmlStream.pipe(parser)
|
||||
})
|
||||
|
||||
it('should properly parse a huge file.', function (done) {
|
||||
var xmlStream = fs.createReadStream('./test/TestFiles/hugeFile.xml')
|
||||
var parser = new ParserFactory({resourcePath: '/items/item'})
|
||||
// console.log(parser)
|
||||
var dataEventCount = 0
|
||||
parser.on('data', function (data) {
|
||||
dataEventCount++
|
||||
})
|
||||
|
||||
parser.on('error', function (err) {
|
||||
done(err)
|
||||
})
|
||||
|
||||
parser.on('end', function () {
|
||||
// console.log('dataEventCount=', dataEventCount)
|
||||
dataEventCount.should.equal(2072)
|
||||
done()
|
||||
})
|
||||
xmlStream.pipe(parser)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user