Files
xml-streamer/parser.js
2016-11-07 01:54:36 +05:30

156 lines
4.0 KiB
JavaScript

var expat = require('node-expat')
var _ = require('lodash')
var ParserState = require('./parserState')
function XmlParser (xmlStream, 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()
}
this.parser.restart = function () {
scope.parser.resume()
xmlStream.resume()
}
process.nextTick(function () {
parse.call(scope, xmlStream)
})
return this.parser
}
function parse (xmlStream) {
if (!this.opts.resourcePath) this.parser.emit('error', new Error('resourcePath missing'))
var scope = this
var parser = scope.parser
var state = this.parserState
var lastIndex
var resourcePath = this.opts.resourcePath
parser.on('startElement', function (name, attrs) {
if (state.isRootNode) validateResourcePath(name)
state.currentPath = state.currentPath + '/' + name
checkForResourcePath(name)
if (state.isPathfound) processStartElement(name, attrs)
})
parser.on('endElement', function (name) {
state.lastEndedNode = name
lastIndex = state.currentPath.lastIndexOf('/' + name)
state.currentPath = state.currentPath.substring(0, lastIndex)
if (state.isPathfound) processEndElement(name)
checkForResourcePath(name)
})
parser.on('text', function (text) {
if (state.isPathfound) processText(text)
})
parser.on('end', function () {
parser.emit('finish')
})
function processStartElement (name, attrs) {
if (!name) return
var obj = {}
if (attrs && !_.isEmpty(attrs)) obj.$ = attrs
var tempObj = state.object
var path = getRelativePath(name)
if (!path) {
if (attrs && !_.isEmpty(attrs)) state.object.$ = attrs
return
}
var tokens = path.split('.')
for (var i = 0; i < tokens.length; i++) {
if (tempObj[tokens[i]]) {
tempObj = tempObj[tokens[i]]
} else {
tempObj[tokens[i]] = []
tempObj = tempObj[tokens[i]]
}
if (Array.isArray(tempObj) && i !== tokens.length - 1) tempObj = tempObj[tempObj.length - 1]
}
tempObj.push(obj)
}
function processEndElement (name) {
var index = resourcePath.lastIndexOf('/')
var rpath = resourcePath.substring(0, index)
if (rpath === state.currentPath) {
if (scope.opts.emitEventsOnNodeName) parser.emit(name, state.object)
parser.emit('data', state.object)
state.object = {}
}
}
function processText (text) {
if (!text || !/\S/.test(text)) {
return
}
var path = getRelativePath()
var tempObj = state.object
if (!path) {
if (!state.object._) state.object._ = ''
state.object._ = state.object._ + text
return
}
var tokens = path.split('.')
for (var i = 0; i < tokens.length; i++) {
if (tempObj[tokens[i]]) {
tempObj = tempObj[tokens[i]]
} else {
tempObj[tokens[i]] = []
tempObj = tempObj[tokens[i]]
}
if (Array.isArray(tempObj) && i !== tokens.length - 1) tempObj = tempObj[tempObj.length - 1]
}
var obj = tempObj[tempObj.length - 1]
if (!obj._) obj._ = ''
obj._ = obj._ + text
}
function checkForResourcePath (name) {
if (state.currentPath.indexOf(resourcePath) === 0) {
state.isPathfound = true
} else {
state.isPathfound = false
}
}
function getRelativePath () {
var xpath = state.currentPath.substring(resourcePath.length)
if (!xpath) return
if (xpath[0] === '/') xpath = xpath.substring(1)
var tokens = xpath.split('/')
var jsonPath = tokens.join('.')
return jsonPath
}
function validateResourcePath (name) {
var temp
var index
state.isRootNode = false
if (resourcePath[0] === '/') {
temp = resourcePath.substring(1, resourcePath.length)
} else {
temp = resourcePath
}
index = temp.indexOf('/')
temp = temp.substring(0, index)
if (temp !== name) {
xmlStream.end()
}
}
}
module.exports = XmlParser