Reformat files using prettier

This commit is contained in:
T. R. Bernstein
2025-07-01 23:21:56 +02:00
parent b1d36437ea
commit 38f7230ce1
18 changed files with 1718 additions and 1606 deletions

View File

@@ -1,229 +1,228 @@
// Source: https://github.com/xmppjs/ltx/blob/master/lib/parsers/ltx.js // Source: https://github.com/xmppjs/ltx/blob/master/lib/parsers/ltx.js
import events from "events"; import events from 'events'
import { unescapeXML } from "./unescape"; import { unescapeXML } from './unescape'
const STATE_TEXT = 0; const STATE_TEXT = 0
const STATE_IGNORE_COMMENT = 1; const STATE_IGNORE_COMMENT = 1
const STATE_IGNORE_INSTRUCTION = 2; const STATE_IGNORE_INSTRUCTION = 2
const STATE_TAG_NAME = 3; const STATE_TAG_NAME = 3
const STATE_TAG = 4; const STATE_TAG = 4
const STATE_ATTR_NAME = 5; const STATE_ATTR_NAME = 5
const STATE_ATTR_EQ = 6; const STATE_ATTR_EQ = 6
const STATE_ATTR_QUOT = 7; const STATE_ATTR_QUOT = 7
const STATE_ATTR_VALUE = 8; const STATE_ATTR_VALUE = 8
const STATE_CDATA = 9; const STATE_CDATA = 9
const lineCounterRegExp = new RegExp("\n", "g"); const lineCounterRegExp = new RegExp('\n', 'g')
export class SaxLtx extends events.EventEmitter { export class SaxLtx extends events.EventEmitter {
public remainder: string; public remainder: string
public tagName: string; public tagName: string
public attrs: any; public attrs: any
public endTag: boolean; public endTag: boolean
public selfClosing: boolean; public selfClosing: boolean
public attrQuote: number; public attrQuote: number
public attrQuoteChar: string; public attrQuoteChar: string
public recordStart = 0; public recordStart = 0
public attrName: string; public attrName: string
public state = STATE_TEXT; public state = STATE_TEXT
public currentLineNumber = 0; public currentLineNumber = 0
constructor() { constructor() {
super(); super()
} }
public getCurrentLineNumber() { public getCurrentLineNumber() {
return this.currentLineNumber + 1; return this.currentLineNumber + 1
} }
public end(data?: Buffer) { public end(data?: Buffer) {
if (data) { if (data) {
this.write(data); this.write(data)
} }
this.removeAllListeners(); this.removeAllListeners()
} }
public write(data: Buffer | string) { public write(data: Buffer | string) {
if (typeof data !== "string") { if (typeof data !== 'string') {
data = data.toString(); data = data.toString()
} }
let pos = 0; let pos = 0
const self = this; const self = this
/* Anything from previous write()? */ /* Anything from previous write()? */
if (self.remainder) { if (self.remainder) {
data = self.remainder + data; data = self.remainder + data
pos += self.remainder.length; pos += self.remainder.length
self.remainder = null; self.remainder = null
} }
function endRecording() { function endRecording() {
if (typeof self.recordStart === "number") { if (typeof self.recordStart === 'number') {
const recorded = (data as string).substring(self.recordStart, pos); const recorded = (data as string).substring(self.recordStart, pos)
self.recordStart = undefined; self.recordStart = undefined
return recorded; return recorded
} }
} }
let prevPos = pos; let prevPos = pos
for (; pos < data.length; pos++) { for (; pos < data.length; pos++) {
if (self.state === STATE_TEXT) { if (self.state === STATE_TEXT) {
// if we're looping through text, fast-forward using indexOf to // if we're looping through text, fast-forward using indexOf to
// the next '<' character // the next '<' character
const lt = data.indexOf("<", pos); const lt = data.indexOf('<', pos)
if (lt !== -1 && pos !== lt) { if (lt !== -1 && pos !== lt) {
pos = lt; pos = lt
} }
} else if (self.state === STATE_ATTR_VALUE) { } else if (self.state === STATE_ATTR_VALUE) {
// if we're looping through an attribute, fast-forward using // if we're looping through an attribute, fast-forward using
// indexOf to the next end quote character // indexOf to the next end quote character
const quot = data.indexOf(self.attrQuoteChar, pos); const quot = data.indexOf(self.attrQuoteChar, pos)
if (quot !== -1) { if (quot !== -1) {
pos = quot; pos = quot
} }
} else if (self.state === STATE_IGNORE_COMMENT) { } else if (self.state === STATE_IGNORE_COMMENT) {
// if we're looping through a comment, fast-forward using // if we're looping through a comment, fast-forward using
// indexOf to the first end-comment character // indexOf to the first end-comment character
const endcomment = data.indexOf("-->", pos); const endcomment = data.indexOf('-->', pos)
if (endcomment !== -1) { if (endcomment !== -1) {
pos = endcomment + 2; // target the '>' character pos = endcomment + 2 // target the '>' character
} }
} }
const newLines = (data.substring(prevPos, pos + 1).match(lineCounterRegExp) || []).length; const newLines = (data.substring(prevPos, pos + 1).match(lineCounterRegExp) || []).length
self.currentLineNumber += newLines; self.currentLineNumber += newLines
prevPos = pos; prevPos = pos
const c = data.charCodeAt(pos); const c = data.charCodeAt(pos)
switch (self.state) { switch (self.state) {
case STATE_TEXT: case STATE_TEXT:
if (c === 60 /* < */) { if (c === 60 /* < */) {
const text = endRecording(); const text = endRecording()
if (text) { if (text) {
self.emit("text", unescapeXML(text)); self.emit('text', unescapeXML(text))
} }
self.state = STATE_TAG_NAME; self.state = STATE_TAG_NAME
self.recordStart = pos + 1; self.recordStart = pos + 1
self.attrs = {}; self.attrs = {}
} }
break; break
case STATE_CDATA: case STATE_CDATA:
if (c === 93 /* ] */ && data.substr(pos + 1, 2) === "]>") { if (c === 93 /* ] */ && data.substr(pos + 1, 2) === ']>') {
const cData = endRecording(); const cData = endRecording()
if (cData) { if (cData) {
self.emit("text", cData); self.emit('text', cData)
} }
self.state = STATE_IGNORE_COMMENT; self.state = STATE_IGNORE_COMMENT
} }
break; break
case STATE_TAG_NAME: case STATE_TAG_NAME:
if (c === 47 /* / */ && self.recordStart === pos) { if (c === 47 /* / */ && self.recordStart === pos) {
self.recordStart = pos + 1; self.recordStart = pos + 1
self.endTag = true; self.endTag = true
} else if (c === 33 /* ! */) { } else if (c === 33 /* ! */) {
if (data.substr(pos + 1, 7) === "[CDATA[") { if (data.substr(pos + 1, 7) === '[CDATA[') {
self.recordStart = pos + 8; self.recordStart = pos + 8
self.state = STATE_CDATA; self.state = STATE_CDATA
} else if (data.substr(pos + 1, 7) === "DOCTYPE") { } else if (data.substr(pos + 1, 7) === 'DOCTYPE') {
self.recordStart = pos + 8; self.recordStart = pos + 8
self.state = STATE_TEXT; self.state = STATE_TEXT
} else { } else {
self.recordStart = undefined; self.recordStart = undefined
self.state = STATE_IGNORE_COMMENT; self.state = STATE_IGNORE_COMMENT
} }
} else if (c === 63 /* ? */) { } else if (c === 63 /* ? */) {
self.recordStart = undefined; self.recordStart = undefined
self.state = STATE_IGNORE_INSTRUCTION; self.state = STATE_IGNORE_INSTRUCTION
} else if (c <= 32 || c === 47 /* / */ || c === 62 /* > */) { } else if (c <= 32 || c === 47 /* / */ || c === 62 /* > */) {
self.tagName = endRecording(); self.tagName = endRecording()
pos--; pos--
self.state = STATE_TAG; self.state = STATE_TAG
} }
break; break
case STATE_IGNORE_COMMENT: case STATE_IGNORE_COMMENT:
if (c === 62 /* > */) { if (c === 62 /* > */) {
const prevFirst = data.charCodeAt(pos - 1); const prevFirst = data.charCodeAt(pos - 1)
const prevSecond = data.charCodeAt(pos - 2); const prevSecond = data.charCodeAt(pos - 2)
if ((prevFirst === 45 /* - */ && prevSecond === 45 /* - */) || if (
(prevFirst === 93 /* ] */ && prevSecond === 93 /* ] */)) { (prevFirst === 45 /* - */ && prevSecond === 45) /* - */ ||
self.state = STATE_TEXT; (prevFirst === 93 /* ] */ && prevSecond === 93) /* ] */
) {
self.state = STATE_TEXT
} }
} }
break; break
case STATE_IGNORE_INSTRUCTION: case STATE_IGNORE_INSTRUCTION:
if (c === 62 /* > */) { if (c === 62 /* > */) {
const prev = data.charCodeAt(pos - 1); const prev = data.charCodeAt(pos - 1)
if (prev === 63 /* ? */) { if (prev === 63 /* ? */) {
self.state = STATE_TEXT; self.state = STATE_TEXT
} }
} }
break; break
case STATE_TAG: case STATE_TAG:
if (c === 62 /* > */) { if (c === 62 /* > */) {
self._handleTagOpening(self.endTag, self.tagName, self.attrs); self._handleTagOpening(self.endTag, self.tagName, self.attrs)
self.tagName = undefined; self.tagName = undefined
self.attrs = undefined; self.attrs = undefined
self.endTag = undefined; self.endTag = undefined
self.selfClosing = undefined; self.selfClosing = undefined
self.state = STATE_TEXT; self.state = STATE_TEXT
self.recordStart = pos + 1; self.recordStart = pos + 1
} else if (c === 47 /* / */) { } else if (c === 47 /* / */) {
self.selfClosing = true; self.selfClosing = true
} else if (c > 32) { } else if (c > 32) {
self.recordStart = pos; self.recordStart = pos
self.state = STATE_ATTR_NAME; self.state = STATE_ATTR_NAME
} }
break; break
case STATE_ATTR_NAME: case STATE_ATTR_NAME:
if (c <= 32 || c === 61 /* = */) { if (c <= 32 || c === 61 /* = */) {
self.attrName = endRecording(); self.attrName = endRecording()
pos--; pos--
self.state = STATE_ATTR_EQ; self.state = STATE_ATTR_EQ
} }
break; break
case STATE_ATTR_EQ: case STATE_ATTR_EQ:
if (c === 61 /* = */) { if (c === 61 /* = */) {
self.state = STATE_ATTR_QUOT; self.state = STATE_ATTR_QUOT
} }
break; break
case STATE_ATTR_QUOT: case STATE_ATTR_QUOT:
if (c === 34 /* " */ || c === 39 /* ' */) { if (c === 34 /* " */ || c === 39 /* ' */) {
self.attrQuote = c; self.attrQuote = c
self.attrQuoteChar = c === 34 ? '"' : "'"; self.attrQuoteChar = c === 34 ? '"' : "'"
self.state = STATE_ATTR_VALUE; self.state = STATE_ATTR_VALUE
self.recordStart = pos + 1; self.recordStart = pos + 1
} }
break; break
case STATE_ATTR_VALUE: case STATE_ATTR_VALUE:
if (c === self.attrQuote) { if (c === self.attrQuote) {
const value = unescapeXML(endRecording()); const value = unescapeXML(endRecording())
self.attrs[self.attrName] = value; self.attrs[self.attrName] = value
self.attrName = undefined; self.attrName = undefined
self.state = STATE_TAG; self.state = STATE_TAG
} }
break; break
} }
} }
if (typeof self.recordStart === "number" && if (typeof self.recordStart === 'number' && self.recordStart <= data.length) {
self.recordStart <= data.length) { self.remainder = data.slice(self.recordStart)
self.remainder = data.slice(self.recordStart); self.recordStart = 0
self.recordStart = 0;
} }
} }
private _handleTagOpening(endTag: boolean, tagName: string, attrs: string) { private _handleTagOpening(endTag: boolean, tagName: string, attrs: string) {
if (!endTag) { if (!endTag) {
this.emit("startElement", tagName, attrs); this.emit('startElement', tagName, attrs)
if (this.selfClosing) { if (this.selfClosing) {
this.emit("endElement", tagName); this.emit('endElement', tagName)
} }
} else { } else {
this.emit("endElement", tagName); this.emit('endElement', tagName)
} }
} }
} }

View File

@@ -1,56 +1,55 @@
import _ from "lodash"; import _ from 'lodash'
import stream from "stream"; import stream from 'stream'
import util from "util"; import util from 'util'
import { SaxLtx } from "./ltx"; import { SaxLtx } from './ltx'
import { ParserState } from "./parserState"; import { ParserState } from './parserState'
const defaults = { const defaults = {
resourcePath: "", resourcePath: '',
emitOnNodeName: false, emitOnNodeName: false,
attrsKey: "$", attrsKey: '$',
textKey: "_", textKey: '_',
explicitArray: true, explicitArray: true,
verbatimText: false, verbatimText: false,
preserveWhitespace: false preserveWhitespace: false
}; }
export interface IXmlParserOptions { export interface IXmlParserOptions {
/** /**
* Optional field. Used to extract the XML nodes that you are interested in. * Optional field. Used to extract the XML nodes that you are interested in.
* *
* @type {string} * @type {string}
* @memberof IXmlParserOptions * @memberof IXmlParserOptions
*/ */
resourcePath?: string; resourcePath?: string
/** /**
* Optional field. Set this to true if you want to listen on node names instead of data event. default: false * Optional field. Set this to true if you want to listen on node names instead of data event. default: false
* *
* @type {boolean} * @type {boolean}
* @memberof IXmlParserOptions * @memberof IXmlParserOptions
*/ */
emitOnNodeName?: boolean; emitOnNodeName?: boolean
/** /**
* Optional field. pass the value with which you want to reference attributes of a node in its object form. default: '$' * Optional field. pass the value with which you want to reference attributes of a node in its object form. default: '$'
* *
* @type {string} * @type {string}
* @memberof IXmlParserOptions * @memberof IXmlParserOptions
*/ */
attrsKey?: string; attrsKey?: string
/** /**
* Optional field. pass the value with which you want to reference node value in its object form. default: '_' * Optional field. pass the value with which you want to reference node value in its object form. default: '_'
* *
* @type {string} * @type {string}
* @memberof IXmlParserOptions * @memberof IXmlParserOptions
*/ */
textKey?: string; textKey?: string
/** /**
* Optional field. Default value is true. All children nodes will come in an array when this option is true. * Optional field. Default value is true. All children nodes will come in an array when this option is true.
* *
* @type {boolean} * @type {boolean}
* @memberof IXmlParserOptions * @memberof IXmlParserOptions
*/ */
explicitArray?: boolean; explicitArray?: boolean
/** /**
* Optional field. Default value is false. When set, text attribute will include all blanks found in xml. * Optional field. Default value is false. When set, text attribute will include all blanks found in xml.
* When unset, blanks are removed as long as they come in one expat single block (blank lines, newlines and entities). * When unset, blanks are removed as long as they come in one expat single block (blank lines, newlines and entities).
@@ -58,299 +57,334 @@ export interface IXmlParserOptions {
* @type {boolean} * @type {boolean}
* @memberof IXmlParserOptions * @memberof IXmlParserOptions
*/ */
verbatimText?: boolean; verbatimText?: boolean
preserveWhitespace?: boolean; preserveWhitespace?: boolean
} }
export class XmlParser extends stream.Transform { export class XmlParser extends stream.Transform {
public parserState: ParserState; public parserState: ParserState
private opts: IXmlParserOptions; private opts: IXmlParserOptions
private _readableState: { objectMode: true, buffer: any }; private _readableState: { objectMode: true; buffer: any }
private parser: SaxLtx; private parser: SaxLtx
constructor(opts?: IXmlParserOptions) { constructor(opts?: IXmlParserOptions) {
super(); super()
this.opts = _.defaults(opts, defaults); this.opts = _.defaults(opts, defaults)
this.parserState = new ParserState(); this.parserState = new ParserState()
this.parser = new SaxLtx(); this.parser = new SaxLtx()
this._readableState.objectMode = true; this._readableState.objectMode = true
} }
public _flush(callback: () => void) { public _flush(callback: () => void) {
this.processChunk(""); this.processChunk('')
callback(); callback()
} }
public _transform(chunk: Buffer | string, encoding: string, callback: () => void) { public _transform(chunk: Buffer | string, encoding: string, callback: () => void) {
if (encoding !== "buffer") { this.emit("error", new Error("unsupported encoding")); } if (encoding !== 'buffer') {
this.emit('error', new Error('unsupported encoding'))
}
this.processChunk(chunk); this.processChunk(chunk)
callback(); callback()
} }
public parse(chunk: Buffer | string, cb: (error: Error, data?: Buffer) => void) { public parse(chunk: Buffer | string, cb: (error: Error, data?: Buffer) => void) {
const parser = this.parser; const parser = this.parser
const state = this.parserState; const state = this.parserState
let error; let error
if (state.isRootNode) { if (state.isRootNode) {
this.checkForInterestedNodeListeners(); this.checkForInterestedNodeListeners()
registerEvents.call(this); registerEvents.call(this)
} }
this.on("error", (err) => { this.on('error', (err) => {
error = err; error = err
}); })
if (chunk.length === 0) { if (chunk.length === 0) {
parser.end(); parser.end()
this.emit("end"); this.emit('end')
this.removeAllListeners(); this.removeAllListeners()
} }
parser.write(chunk); parser.write(chunk)
if (error) { return cb(error); } if (error) {
return cb(error)
}
const result = []; const result = []
while (this._readableState.buffer.length > 0) { while (this._readableState.buffer.length > 0) {
result.push(this._readableState.buffer.consume()); result.push(this._readableState.buffer.consume())
} }
return cb(null, result as any); return cb(null, result as any)
} }
private processChunk(chunk: string | Buffer) { private processChunk(chunk: string | Buffer) {
const parser = this.parser; const parser = this.parser
const state = this.parserState; const state = this.parserState
if (state.isRootNode) { if (state.isRootNode) {
this.checkForInterestedNodeListeners(); this.checkForInterestedNodeListeners()
registerEvents.call(this); registerEvents.call(this)
} }
parser.write(chunk); parser.write(chunk)
} }
private checkForInterestedNodeListeners() { private checkForInterestedNodeListeners() {
const ignore = ["end", "prefinish", "data", "error"]; const ignore = ['end', 'prefinish', 'data', 'error']
const eventNames = Object.keys((this as any)._events); const eventNames = Object.keys((this as any)._events)
// tslint:disable-next-line:prefer-for-of // tslint:disable-next-line:prefer-for-of
for (let i = 0; i < eventNames.length; i++) { for (let i = 0; i < eventNames.length; i++) {
if (_.includes(ignore, eventNames[i], 0)) { continue; } if (_.includes(ignore, eventNames[i], 0)) {
this.parserState.interestedNodes.push(eventNames[i]); continue
}
this.parserState.interestedNodes.push(eventNames[i])
} }
} }
} }
function registerEvents() { function registerEvents() {
const scope = this; const scope = this
const parser: SaxLtx = this.parser; const parser: SaxLtx = this.parser
const state: ParserState = this.parserState; const state: ParserState = this.parserState
let lastIndex; let lastIndex
const resourcePath = this.opts.resourcePath; const resourcePath = this.opts.resourcePath
const attrsKey = this.opts.attrsKey; const attrsKey = this.opts.attrsKey
const textKey = this.opts.textKey; const textKey = this.opts.textKey
const interestedNodes = state.interestedNodes; const interestedNodes = state.interestedNodes
const explicitArray = this.opts.explicitArray; const explicitArray = this.opts.explicitArray
const verbatimText = this.opts.verbatimText; const verbatimText = this.opts.verbatimText
const preserveWhitespace = this.opts.preserveWhitespace; const preserveWhitespace = this.opts.preserveWhitespace
parser.on("startElement", (name, attrs) => { parser.on('startElement', (name, attrs) => {
if (state.isRootNode) { state.isRootNode = false; } if (state.isRootNode) {
state.currentPath = state.currentPath + "/" + name; state.isRootNode = false
checkForResourcePath(name);
if (state.isPathfound) { processStartElement(name, attrs); }
});
parser.on("endElement", (name) => {
state.lastEndedNode = name;
lastIndex = state.currentPath.lastIndexOf("/" + name);
if (state.currentPath.substring(lastIndex + 1).indexOf("/") !== -1) {
processError.call(this, `mismatched tag`);
} }
state.currentPath = state.currentPath.substring(0, lastIndex); state.currentPath = state.currentPath + '/' + name
if (state.isPathfound) { processEndElement(name); } checkForResourcePath(name)
checkForResourcePath(name); if (state.isPathfound) {
}); processStartElement(name, attrs)
}
})
parser.on("text", (text) => { parser.on('endElement', (name) => {
if (state.isPathfound) { processText(text); } state.lastEndedNode = name
}); lastIndex = state.currentPath.lastIndexOf('/' + name)
if (state.currentPath.substring(lastIndex + 1).indexOf('/') !== -1) {
processError.call(this, `mismatched tag`)
}
state.currentPath = state.currentPath.substring(0, lastIndex)
if (state.isPathfound) {
processEndElement(name)
}
checkForResourcePath(name)
})
parser.on("error", function(err) { parser.on('text', (text) => {
processError.call(this, err); if (state.isPathfound) {
}); processText(text)
}
})
parser.on('error', function (err) {
processError.call(this, err)
})
function processStartElement(name: string, attrs: any) { function processStartElement(name: string, attrs: any) {
if (!name) { return; } if (!name) {
return
const obj: any = {};
if (attrs && !_.isEmpty(attrs)) { obj[attrsKey] = attrs; }
let tempObj = state.object;
const path = getRelativePath(/*name*/);
if (!path) {
if (attrs && !_.isEmpty(attrs)) { state.object[attrsKey] = attrs; }
return;
} }
const tokens = path.split(".");
const obj: any = {}
if (attrs && !_.isEmpty(attrs)) {
obj[attrsKey] = attrs
}
let tempObj = state.object
const path = getRelativePath(/*name*/)
if (!path) {
if (attrs && !_.isEmpty(attrs)) {
state.object[attrsKey] = attrs
}
return
}
const tokens = path.split('.')
for (let i = 0; i < tokens.length; i++) { for (let i = 0; i < tokens.length; i++) {
if (tempObj[tokens[i]] && !(explicitArray === false && i === tokens.length - 1)) { if (tempObj[tokens[i]] && !(explicitArray === false && i === tokens.length - 1)) {
tempObj = tempObj[tokens[i]]; tempObj = tempObj[tokens[i]]
} else { } else {
// if explicitArray is true then create each node as array // if explicitArray is true then create each node as array
// irrespective of how many nodes are there with same name. // irrespective of how many nodes are there with same name.
tempObj[tokens[i]] = explicitArray ? [] : obj; tempObj[tokens[i]] = explicitArray ? [] : obj
tempObj = tempObj[tokens[i]]; tempObj = tempObj[tokens[i]]
}
if (Array.isArray(tempObj) && i !== tokens.length - 1) {
tempObj = tempObj[tempObj.length - 1]
} }
if (Array.isArray(tempObj) && i !== tokens.length - 1) { tempObj = tempObj[tempObj.length - 1]; }
} }
if (Array.isArray(tempObj)) { if (Array.isArray(tempObj)) {
tempObj.push(obj); tempObj.push(obj)
} }
} }
function processEndElement(name: string) { function processEndElement(name: string) {
if (resourcePath) { if (resourcePath) {
const index = resourcePath.lastIndexOf("/"); const index = resourcePath.lastIndexOf('/')
const rpath = resourcePath.substring(0, index); const rpath = resourcePath.substring(0, index)
if (rpath === state.currentPath) { if (rpath === state.currentPath) {
scope.push(state.object); scope.push(state.object)
if (scope.opts.emitOnNodeName) { scope.emit(name, state.object); } if (scope.opts.emitOnNodeName) {
state.object = {}; scope.emit(name, state.object)
}
state.object = {}
} }
} else { } else {
if (_.includes(interestedNodes, name, 0)) { if (_.includes(interestedNodes, name, 0)) {
emitInterestedNode(name); emitInterestedNode(name)
if (state.firstFoundNode === name) { if (state.firstFoundNode === name) {
state.object = {}; state.object = {}
state.firstFoundNode = ""; state.firstFoundNode = ''
state.isPathfound = false; state.isPathfound = false
} }
} }
} }
} }
function emitInterestedNode(name: string) { function emitInterestedNode(name: string) {
let index; let index
let xpath; let xpath
let pathTokens; let pathTokens
xpath = state.currentPath.substring(1); xpath = state.currentPath.substring(1)
pathTokens = xpath.split("/"); pathTokens = xpath.split('/')
pathTokens.push(name); pathTokens.push(name)
index = pathTokens.indexOf(state.firstFoundNode); index = pathTokens.indexOf(state.firstFoundNode)
pathTokens = _.drop(pathTokens, index + 1); pathTokens = _.drop(pathTokens, index + 1)
let tempObj = state.object; let tempObj = state.object
// tslint:disable-next-line:prefer-for-of // tslint:disable-next-line:prefer-for-of
for (let i = 0; i < pathTokens.length; i++) { for (let i = 0; i < pathTokens.length; i++) {
tempObj = tempObj[pathTokens[i] as any]; tempObj = tempObj[pathTokens[i] as any]
}
if (Array.isArray(tempObj)) {
tempObj = tempObj[tempObj.length - 1]
} }
if (Array.isArray(tempObj)) { tempObj = tempObj[tempObj.length - 1]; }
scope.emit(name, tempObj); scope.emit(name, tempObj)
scope.push(tempObj); scope.push(tempObj)
} }
function processText(text: string) { function processText(text: string) {
if ((!text) || ((!verbatimText) && !/\S/.test(text))) { if (!text || (!verbatimText && !/\S/.test(text))) {
return; return
} }
const path = getRelativePath(); const path = getRelativePath()
let tempObj = state.object; let tempObj = state.object
if (!path) { if (!path) {
if (!state.object[textKey]) { state.object[textKey] = ""; } if (!state.object[textKey]) {
state.object[textKey] = state.object[textKey] + text; state.object[textKey] = ''
if ((!preserveWhitespace)) {
state.object[textKey] = state.object[textKey].replace(/\s+/g, " ").trim();
} }
return; state.object[textKey] = state.object[textKey] + text
if (!preserveWhitespace) {
state.object[textKey] = state.object[textKey].replace(/\s+/g, ' ').trim()
}
return
} }
const tokens = path.split("."); const tokens = path.split('.')
for (let i = 0; i < tokens.length; i++) { for (let i = 0; i < tokens.length; i++) {
if (tempObj[tokens[i]]) { if (tempObj[tokens[i]]) {
tempObj = tempObj[tokens[i]]; tempObj = tempObj[tokens[i]]
} else { } else {
tempObj[tokens[i]] = explicitArray ? [] : {}; tempObj[tokens[i]] = explicitArray ? [] : {}
tempObj = tempObj[tokens[i]]; tempObj = tempObj[tokens[i]]
}
if (Array.isArray(tempObj) && i !== tokens.length - 1) {
tempObj = tempObj[tempObj.length - 1]
} }
if (Array.isArray(tempObj) && i !== tokens.length - 1) { tempObj = tempObj[tempObj.length - 1]; }
} }
if (Array.isArray(tempObj)) { if (Array.isArray(tempObj)) {
const obj = tempObj[tempObj.length - 1]; const obj = tempObj[tempObj.length - 1]
if (!obj[textKey]) { obj[textKey] = ""; } if (!obj[textKey]) {
obj[textKey] = obj[textKey] + text; obj[textKey] = ''
if ((!preserveWhitespace)) {
obj[textKey] = obj[textKey].replace(/\s+/g, " ").trim();
} }
obj[textKey] = obj[textKey] + text
if (!preserveWhitespace) {
obj[textKey] = obj[textKey].replace(/\s+/g, ' ').trim()
}
} else { } else {
if (!tempObj[textKey]) { tempObj[textKey] = ""; } if (!tempObj[textKey]) {
tempObj[textKey] = tempObj[textKey] + text; tempObj[textKey] = ''
}
tempObj[textKey] = tempObj[textKey] + text
if ((!preserveWhitespace)) { if (!preserveWhitespace) {
tempObj[textKey] = tempObj[textKey].replace(/\s+/g, " ").trim(); tempObj[textKey] = tempObj[textKey].replace(/\s+/g, ' ').trim()
} }
} }
} }
function checkForResourcePath(name: string) { function checkForResourcePath(name: string) {
if (resourcePath) { if (resourcePath) {
if (state.currentPath.indexOf(resourcePath) === 0) { if (state.currentPath.indexOf(resourcePath) === 0) {
state.isPathfound = true; state.isPathfound = true
} else { } else {
state.isPathfound = false; state.isPathfound = false
} }
} else { } else {
if (_.includes(interestedNodes, name, 0)) { if (_.includes(interestedNodes, name, 0)) {
state.isPathfound = true; state.isPathfound = true
if (!state.firstFoundNode) { if (!state.firstFoundNode) {
state.firstFoundNode = name; state.firstFoundNode = name
} }
} }
} }
} }
function getRelativePath() { function getRelativePath() {
let tokens; let tokens
let jsonPath; let jsonPath
let index; let index
if (resourcePath) { if (resourcePath) {
let xpath = state.currentPath.substring(resourcePath.length); let xpath = state.currentPath.substring(resourcePath.length)
if (!xpath) { return; } if (!xpath) {
if (xpath[0] === "/") { xpath = xpath.substring(1); } return
tokens = xpath.split("/"); }
jsonPath = tokens.join("."); if (xpath[0] === '/') {
xpath = xpath.substring(1)
}
tokens = xpath.split('/')
jsonPath = tokens.join('.')
} else { } else {
const xpath = state.currentPath.substring(1); const xpath = state.currentPath.substring(1)
tokens = xpath.split("/"); tokens = xpath.split('/')
index = tokens.indexOf(state.firstFoundNode); index = tokens.indexOf(state.firstFoundNode)
tokens = _.drop(tokens, index + 1); tokens = _.drop(tokens, index + 1)
jsonPath = tokens.join("."); jsonPath = tokens.join('.')
} }
return jsonPath; return jsonPath
} }
} }
function processError(err: Error) { function processError(err: Error) {
const parser = this.parser; const parser = this.parser
let error: Error = null; let error: Error = null
if (err) { if (err) {
error = err; error = err
} else { } else {
error = parser.getError(); error = parser.getError()
} }
error = new Error(`${error} at line no: ${parser.getCurrentLineNumber()}`); error = new Error(`${error} at line no: ${parser.getCurrentLineNumber()}`)
this.emit("error", error); this.emit('error', error)
return error; return error
} }

View File

@@ -1,11 +1,10 @@
export class ParserState { export class ParserState {
public currentPath = ""; public currentPath = ''
public lastEndedNode = ""; public lastEndedNode = ''
public isPathfound = false; public isPathfound = false
public object: any = {}; public object: any = {}
public paused = false; public paused = false
public isRootNode = true; public isRootNode = true
public firstFoundNode = ""; public firstFoundNode = ''
public interestedNodes: string[] = []; public interestedNodes: string[] = []
} }

View File

@@ -1,83 +1,86 @@
const escapeXMLTable: { [char: string]: string } = {
const escapeXMLTable: {[char: string]: string} = { '&': '&amp;',
"&": "&amp;", '<': '&lt;',
"<": "&lt;", '>': '&gt;',
">": "&gt;", '"': '&quot;',
'"': "&quot;", "'": '&apos;'
"'": "&apos;"
};
function escapeXMLReplace(match: string) {
return escapeXMLTable[match];
} }
const unescapeXMLTable: {[char: string]: string} = { function escapeXMLReplace(match: string) {
"&amp;": "&", return escapeXMLTable[match]
"&lt;": "<", }
"&gt;": ">",
"&quot;": '"', const unescapeXMLTable: { [char: string]: string } = {
"&apos;": "'" '&amp;': '&',
}; '&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&apos;': "'"
}
function unescapeXMLReplace(match: string) { function unescapeXMLReplace(match: string) {
if (match[1] === "#") { if (match[1] === '#') {
let num; let num
if (match[2] === "x") { if (match[2] === 'x') {
num = parseInt(match.slice(3), 16); num = parseInt(match.slice(3), 16)
} else { } else {
num = parseInt(match.slice(2), 10); num = parseInt(match.slice(2), 10)
} }
// https://www.w3.org/TR/xml/#NT-Char defines legal XML characters: // https://www.w3.org/TR/xml/#NT-Char defines legal XML characters:
// #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
if (num === 0x9 || num === 0xA || num === 0xD || if (
(num >= 0x20 && num <= 0xD7FF) || num === 0x9 ||
(num >= 0xE000 && num <= 0xFFFD) || num === 0xa ||
(num >= 0x10000 && num <= 0x10FFFF)) { num === 0xd ||
return String.fromCodePoint(num); (num >= 0x20 && num <= 0xd7ff) ||
(num >= 0xe000 && num <= 0xfffd) ||
(num >= 0x10000 && num <= 0x10ffff)
) {
return String.fromCodePoint(num)
} }
throw new Error("Illegal XML character 0x" + num.toString(16)); throw new Error('Illegal XML character 0x' + num.toString(16))
} }
if (unescapeXMLTable[match]) { if (unescapeXMLTable[match]) {
return unescapeXMLTable[match] || match; return unescapeXMLTable[match] || match
} }
throw new Error("Illegal XML entity " + match); throw new Error('Illegal XML entity ' + match)
} }
export function escapeXML(s: string) { export function escapeXML(s: string) {
return s.replace(/&|<|>|"|'/g, escapeXMLReplace); return s.replace(/&|<|>|"|'/g, escapeXMLReplace)
} }
export function unescapeXML(s: string) { export function unescapeXML(s: string) {
let result = ""; let result = ''
let start = -1; let start = -1
let end = -1; let end = -1
let previous = 0; let previous = 0
start = s.indexOf("&", previous); start = s.indexOf('&', previous)
end = s.indexOf(";", start + 1); end = s.indexOf(';', start + 1)
while ((start !== -1) && (end !== -1 )) { while (start !== -1 && end !== -1) {
result = result + result = result + s.substring(previous, start) + unescapeXMLReplace(s.substring(start, end + 1))
s.substring(previous, start) + previous = end + 1
unescapeXMLReplace(s.substring(start, end + 1)); start = s.indexOf('&', previous)
previous = end + 1; end = s.indexOf(';', start + 1)
start = s.indexOf("&", previous);
end = s.indexOf(";", start + 1);
} }
// shortcut if loop never entered: // shortcut if loop never entered:
// return the original string without creating new objects // return the original string without creating new objects
if (previous === 0) { return s; } if (previous === 0) {
return s
}
// push the remaining characters // push the remaining characters
result = result + s.substring(previous); result = result + s.substring(previous)
return result; return result
} }
export function escapeXMLText(s: string) { export function escapeXMLText(s: string) {
return s.replace(/&|<|>/g, escapeXMLReplace); return s.replace(/&|<|>/g, escapeXMLReplace)
} }
export function unescapeXMLText(s: string) { export function unescapeXMLText(s: string) {
return s.replace(/&(amp|#38|lt|#60|gt|#62);/g, unescapeXMLReplace); return s.replace(/&(amp|#38|lt|#60|gt|#62);/g, unescapeXMLReplace)
} }

View File

@@ -1,146 +1,153 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("Basic behavior", () => { describe('Basic behavior', () => {
it("should properly parse a simple file.", (done) => { it('should properly parse a simple file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
const expectedData = [ const expectedData = [
{ {
$: { id: "1", test: "hello" }, $: { id: '1', test: 'hello' },
subitem: subitem: [
[{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "2" }, _: "two" }] { $: { sub: '2' }, _: 'two' }
]
}, },
{ {
$: { id: "2" }, $: { id: '2' },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
}]; }
const actualData: string[] = []; ]
let dataEventCount = 0; const actualData: string[] = []
let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
should(err).not.be.ok(); should(err).not.be.ok()
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', actualData) // console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
should(dataEventCount).equal(2); should(dataEventCount).equal(2)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a medium size file.", (done) => { it('should properly parse a medium size file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
let dataEventCount = 0; let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(10); should(dataEventCount).equal(10)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a file containing many nodes.", (done) => { it('should properly parse a file containing many nodes.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
let dataEventCount = 0; let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(296); should(dataEventCount).equal(296)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a xml simple file in which nodes contain text values randomly.", (done) => { it('should properly parse a xml simple file in which nodes contain text values randomly.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/randomText.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/randomText.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
const expectedData = [{ const expectedData = [
$: { id: "1", test: "hello" }, _: "item one two", {
subitem: [{ $: { sub: "TESTING SUB" }, _: "one" }, $: { id: '1', test: 'hello' },
{ $: { sub: "2" }, _: "two" }] _: 'item one two',
}, subitem: [
{ { $: { sub: 'TESTING SUB' }, _: 'one' },
$: { id: "2" }, _: "item one two three four", { $: { sub: '2' }, _: 'two' }
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] ]
} },
]; {
const actualData: string[] = []; $: { id: '2' },
let dataEventCount = 0; _: 'item one two three four',
subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
}
]
const actualData: string[] = []
let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1)) // console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
should(dataEventCount).equal(2); should(dataEventCount).equal(2)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a huge file.", (done) => { it('should properly parse a huge file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/hugeFile.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
// console.log(parser) // console.log(parser)
let dataEventCount = 0; let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(2072); should(dataEventCount).equal(2072)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })

View File

@@ -1,30 +1,30 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("CData and comments in xml", () => { describe('CData and comments in xml', () => {
it("should properly parse a simple file.", (done) => { it('should properly parse a simple file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/CData-comments.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/CData-comments.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
let dataEventCount = 0; let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(296); should(dataEventCount).equal(296)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })

View File

@@ -1,136 +1,138 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("emitOnNodeName", () => { describe('emitOnNodeName', () => {
it("should properly emit events on node names.", (done) => { it('should properly emit events on node names.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true }); const parser = new XmlParser({ resourcePath: '/items/item', emitOnNodeName: true })
const expectedData = [ const expectedData = [
{ {
$: { id: "1", test: "hello" }, $: { id: '1', test: 'hello' },
subitem: subitem: [
[{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "2" }, _: "two" }] { $: { sub: '2' }, _: 'two' }
]
}, },
{ {
$: { id: "2" }, $: { id: '2' },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
}]; }
const actualData: string[] = []; ]
const itemData : string[] = []; const actualData: string[] = []
let dataEventCount = 0; const itemData: string[] = []
let itemCount = 0; let dataEventCount = 0
let itemCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
itemData.push(item); itemData.push(item)
itemCount++; itemCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', actualData) // console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
should(dataEventCount).equal(2); should(dataEventCount).equal(2)
should(itemData).deepEqual(expectedData); should(itemData).deepEqual(expectedData)
should(itemCount).equal(2); should(itemCount).equal(2)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly emit events on node names while parsing a medium size file.", (done) => { it('should properly emit events on node names while parsing a medium size file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true }); const parser = new XmlParser({ resourcePath: '/items/item', emitOnNodeName: true })
let dataEventCount = 0; let dataEventCount = 0
let itemCount = 0; let itemCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("item", (data) => { parser.on('item', (data) => {
itemCount++; itemCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(10); should(dataEventCount).equal(10)
should(itemCount).equal(10); should(itemCount).equal(10)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a file containing many nodes.", (done) => { it('should properly parse a file containing many nodes.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true }); const parser = new XmlParser({ resourcePath: '/items/item', emitOnNodeName: true })
let dataEventCount = 0; let dataEventCount = 0
let itemCount = 0; let itemCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("item", (data) => { parser.on('item', (data) => {
itemCount++; itemCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(296); should(dataEventCount).equal(296)
should(itemCount).equal(296); should(itemCount).equal(296)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a huge file.", (done) => { it('should properly parse a huge file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/hugeFile.xml')
const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true }); const parser = new XmlParser({ resourcePath: '/items/item', emitOnNodeName: true })
let dataEventCount = 0; let dataEventCount = 0
let itemCount = 0; let itemCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
itemCount++; itemCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(2072); should(dataEventCount).equal(2072)
should(itemCount).equal(2072); should(itemCount).equal(2072)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })

View File

@@ -1,45 +1,44 @@
import fs from 'fs'
import 'mocha'
import should from 'should'
import stream from 'stream'
import zlib from 'zlib'
import fs from "fs"; import { XmlParser } from '../src/parser'
import "mocha"; describe.skip('Error Handling', () => {
import should from "should"; it('should properly return error if the xml file is corrupted.', (done) => {
import stream from "stream"; const xmlStream = fs.createReadStream('./test/TestFiles/corrupted.xml')
import zlib from "zlib"; const parser = new XmlParser({ resourcePath: '/items/item' })
let dataEventCount = 0
import { XmlParser } from "../src/parser"; parser.on('data', (data) => {
describe.skip("Error Handling", () => { dataEventCount++
it("should properly return error if the xml file is corrupted.", (done) => { })
const xmlStream = fs.createReadStream("./test/TestFiles/corrupted.xml");
const parser = new XmlParser({ resourcePath: "/items/item" });
let dataEventCount = 0;
parser.on("data", (data) => { parser.on('error', (err) => {
dataEventCount++;
});
parser.on("error", (err) => {
// console.log(err) // console.log(err)
should(err.message).equal("mismatched tag at line no: 12"); should(err.message).equal('mismatched tag at line no: 12')
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly return error if the large xml file is corrupted.", (done) => { it('should properly return error if the large xml file is corrupted.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/largeCorruptedFile.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/largeCorruptedFile.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
let dataEventCount = 0; let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
// console.log(err) // console.log(err)
should(err.message).equal("mismatched tag at line no: 8346"); should(err.message).equal('mismatched tag at line no: 8346')
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })

View File

@@ -1,250 +1,270 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("should respect explicitArray constructor option", () => { describe('should respect explicitArray constructor option', () => {
it("should properly parse a simple file with explicitArray set to false.", (done) => { it('should properly parse a simple file with explicitArray set to false.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/item.xml"); const xml = fs.readFileSync('./test/TestFiles/item.xml')
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false }); const parser = new XmlParser({ resourcePath: '/items/item', explicitArray: false })
const expectedData = [ const expectedData = [
{ {
$: { id: "1", test: "hello" }, $: { id: '1', test: 'hello' },
subitem: { $: { sub: "2" }, _: "two" } subitem: { $: { sub: '2' }, _: 'two' }
}, },
{ {
$: { id: "2" }, $: { id: '2' },
subitem: { _: "five" } subitem: { _: 'five' }
}]; }
]
parser.parse(xml.toString(), (err, data) => { parser.parse(xml.toString(), (err, data) => {
if (err) { done(err); } if (err) {
done(err)
}
// console.log('data=', JSON.stringify(data)) // console.log('data=', JSON.stringify(data))
should(data).deepEqual(expectedData); should(data).deepEqual(expectedData)
done(); done()
}); })
}); })
it("should properly parse a medium size file with explicitArray set to false.", (done) => { it('should properly parse a medium size file with explicitArray set to false.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/medium.xml"); const xml = fs.readFileSync('./test/TestFiles/medium.xml')
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false }); const parser = new XmlParser({ resourcePath: '/items/item', explicitArray: false })
const expectedData = [ const expectedData = [
{ {
$: { $: {
id: "1", id: '1',
test: "hello" test: 'hello'
}, },
subitem: { subitem: {
$: { $: {
sub: "2" sub: '2'
}, },
_: "two" _: 'two'
} }
}, },
{ {
$: { $: {
id: "2" id: '2'
}, },
subitem: { subitem: {
_: "five" _: 'five'
} }
}, },
{ {
$: { $: {
id: "3", id: '3',
test: "hello" test: 'hello'
}, },
subitem: { subitem: {
$: { $: {
sub: "2" sub: '2'
}, },
_: "two" _: 'two'
} }
}, },
{ {
$: { $: {
id: "4", id: '4',
test: "hello" test: 'hello'
}, },
subitem: { subitem: {
$: { $: {
sub: "2" sub: '2'
}, },
_: "two" _: 'two'
} }
}, },
{ {
$: { $: {
id: "5", id: '5',
test: "hello" test: 'hello'
}, },
subitem: { subitem: {
$: { $: {
sub: "2" sub: '2'
}, },
_: "two" _: 'two'
} }
}, },
{ {
$: { $: {
id: "6", id: '6',
test: "hello" test: 'hello'
}, },
subitem: { subitem: {
$: { $: {
sub: "2" sub: '2'
}, },
_: "two" _: 'two'
} }
}, },
{ {
$: { $: {
id: "7", id: '7',
test: "hello" test: 'hello'
}, },
subitem: { subitem: {
$: { $: {
sub: "2" sub: '2'
}, },
_: "two" _: 'two'
} }
}, },
{ {
$: { $: {
id: "8", id: '8',
test: "hello" test: 'hello'
}, },
subitem: { subitem: {
$: { $: {
sub: "2" sub: '2'
}, },
_: "two" _: 'two'
} }
}, },
{ {
$: { $: {
id: "9", id: '9',
test: "hello" test: 'hello'
}, },
subitem: { subitem: {
$: { $: {
sub: "2" sub: '2'
}, },
_: "two" _: 'two'
} }
}, },
{ {
$: { $: {
id: "10", id: '10',
test: "hello" test: 'hello'
}, },
subitem: { subitem: {
$: { $: {
sub: "2" sub: '2'
}, },
_: "two" _: 'two'
} }
} }
]; ]
parser.parse(xml, (err, data) => { parser.parse(xml, (err, data) => {
if (err) { done(err); } if (err) {
done(err)
}
should(data).deepEqual(expectedData); should(data).deepEqual(expectedData)
should(data.length).equal(10); should(data.length).equal(10)
done(); done()
}); })
}); })
it("should properly parse a file containing many nodes when explicitArray set to false.", (done) => { it('should properly parse a file containing many nodes when explicitArray set to false.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/manyItems.xml"); const xml = fs.readFileSync('./test/TestFiles/manyItems.xml')
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false }); const parser = new XmlParser({ resourcePath: '/items/item', explicitArray: false })
parser.parse(xml, (err, data) => { parser.parse(xml, (err, data) => {
if (err) { done(err); } if (err) {
done(err)
}
should(data.length).equal(296); should(data.length).equal(296)
done(); done()
}); })
}); })
it("should properly parse a xml simple file in which nodes contain text values randomly when explicitArray set to false.", (done) => { it('should properly parse a xml simple file in which nodes contain text values randomly when explicitArray set to false.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/randomText.xml"); const xml = fs.readFileSync('./test/TestFiles/randomText.xml')
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false }); const parser = new XmlParser({ resourcePath: '/items/item', explicitArray: false })
const expectedData = [{ const expectedData = [
$: { id: "1", test: "hello" }, _: "item one two", {
subitem: { $: { sub: "2" }, _: "two" } $: { id: '1', test: 'hello' },
}, _: 'item one two',
{ subitem: { $: { sub: '2' }, _: 'two' }
$: { id: "2" }, _: "item one two three four", },
subitem: { _: "five" } {
} $: { id: '2' },
]; _: 'item one two three four',
subitem: { _: 'five' }
}
]
parser.parse(xml, (err, data) => { parser.parse(xml, (err, data) => {
if (err) { done(err); } if (err) {
done(err)
}
should(data).deepEqual(expectedData); should(data).deepEqual(expectedData)
should(data.length).equal(2); should(data.length).equal(2)
done(); done()
}); })
}); })
it("should properly parse a huge file with explicitArray set to false.", (done) => { it('should properly parse a huge file with explicitArray set to false.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/hugeFile.xml"); const xml = fs.readFileSync('./test/TestFiles/hugeFile.xml')
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false }); const parser = new XmlParser({ resourcePath: '/items/item', explicitArray: false })
// console.log(parser) // console.log(parser)
parser.parse(xml, (err, data) => { parser.parse(xml, (err, data) => {
if (err) { done(err); } if (err) {
should(data.length).equal(2072); done(err)
done(); }
}); should(data.length).equal(2072)
}); done()
})
})
it("should properly return error if the xml file is corrupted.", (done) => { it('should properly return error if the xml file is corrupted.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/corrupted.xml"); const xml = fs.readFileSync('./test/TestFiles/corrupted.xml')
const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false }); const parser = new XmlParser({ resourcePath: '/items/item', explicitArray: false })
parser.parse(xml, (err, data) => { parser.parse(xml, (err, data) => {
// console.log(err) // console.log(err)
should(err.message).equal("mismatched tag at line no: 12"); should(err.message).equal('mismatched tag at line no: 12')
should(data).not.be.ok(); should(data).not.be.ok()
done(); done()
}); })
}); })
it("should properly generate objects when special symbols are passed as attrs and text keys and explicitArray is false in the options.", (done) => { it('should properly generate objects when special symbols are passed as attrs and text keys and explicitArray is false in the options.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
const parser = new XmlParser({ resourcePath: "/items/item", attrsKey: "!", textKey: "%", explicitArray: false }); const parser = new XmlParser({
resourcePath: '/items/item',
attrsKey: '!',
textKey: '%',
explicitArray: false
})
const expectedData = [ const expectedData = [
{ {
"!": { id: "1", test: "hello" }, '!': { id: '1', test: 'hello' },
"subitem": { "!": { sub: "2" }, "%": "two" } subitem: { '!': { sub: '2' }, '%': 'two' }
}, },
{ {
"!": { id: "2" }, '!': { id: '2' },
"subitem": { "%": "five" } subitem: { '%': 'five' }
}]; }
const actualData: string[] = []; ]
let dataEventCount = 0; const actualData: string[] = []
let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1)) // console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
should(dataEventCount).equal(2); should(dataEventCount).equal(2)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })

View File

@@ -1,321 +1,336 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("interested Nodes", () => { describe('interested Nodes', () => {
it("should properly parse a simple file.", (done) => { it('should properly parse a simple file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
const parser = new XmlParser(); const parser = new XmlParser()
const expectedData = const expectedData = [
[ { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: '2' }, _: 'two' },
{ $: { sub: "2" }, _: "two" }, {
{ $: { id: '1', test: 'hello' },
$: { id: "1", test: "hello" }, subitem: [
subitem: [{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "2" }, _: "two" }] { $: { sub: '2' }, _: 'two' }
}, ]
{ _: "three" }, },
{ _: "four" }, { _: 'three' },
{ _: "five" }, { _: 'four' },
{ { _: 'five' },
$: { id: "2" }, {
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] $: { id: '2' },
} subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
]; }
]
const actualData: string[] = []; const actualData: string[] = []
let dataEventCount = 0; let dataEventCount = 0
const expectedItems = [ const expectedItems = [
{ {
$: { id: "1", test: "hello" }, $: { id: '1', test: 'hello' },
subitem: subitem: [
[{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "2" }, _: "two" }] { $: { sub: '2' }, _: 'two' }
]
}, },
{ {
$: { id: "2" }, $: { id: '2' },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
}]; }
const actualItems: string[] = []; ]
const actualSubitems: string[] = []; const actualItems: string[] = []
const actualSubitems: string[] = []
const expectedSubitems = [ const expectedSubitems = [
{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "2" }, _: "two" }, { $: { sub: '2' }, _: 'two' },
{ _: "three" }, { _: 'three' },
{ _: "four" }, { _: 'four' },
{ _: "five" } { _: 'five' }
]; ]
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
should(err).not.be.ok(); should(err).not.be.ok()
done(err); done(err)
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
actualItems.push(item); actualItems.push(item)
}); })
parser.on("subitem", (subitem) => { parser.on('subitem', (subitem) => {
actualSubitems.push(subitem); actualSubitems.push(subitem)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1)) // console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
should(actualItems).deepEqual(expectedItems); should(actualItems).deepEqual(expectedItems)
should(actualSubitems).deepEqual(expectedSubitems); should(actualSubitems).deepEqual(expectedSubitems)
should(actualSubitems.length).equal(5); should(actualSubitems.length).equal(5)
should(actualItems.length).equal(2); should(actualItems.length).equal(2)
should(dataEventCount).equal(7); should(dataEventCount).equal(7)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a medium size file.", (done) => { it('should properly parse a medium size file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
const parser = new XmlParser(); const parser = new XmlParser()
let dataEventCount = 0; let dataEventCount = 0
let itemEventCount = 0; let itemEventCount = 0
let subitemEventCount = 0; let subitemEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
itemEventCount++; itemEventCount++
}); })
parser.on("subitem", (subitem) => { parser.on('subitem', (subitem) => {
subitemEventCount++; subitemEventCount++
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemEventCount) // console.log('itemEventCount=', itemEventCount)
// console.log('subitemEventCount=', subitemEventCount) // console.log('subitemEventCount=', subitemEventCount)
should(dataEventCount).equal(31); should(dataEventCount).equal(31)
should(itemEventCount).equal(10); should(itemEventCount).equal(10)
should(subitemEventCount).equal(21); should(subitemEventCount).equal(21)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a file containing many nodes.", (done) => { it('should properly parse a file containing many nodes.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
const parser = new XmlParser(); const parser = new XmlParser()
let dataEventCount = 0; let dataEventCount = 0
let itemEventCount = 0; let itemEventCount = 0
let subitemEventCount = 0; let subitemEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
itemEventCount++; itemEventCount++
}); })
parser.on("subitem", (subitem) => { parser.on('subitem', (subitem) => {
subitemEventCount++; subitemEventCount++
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemEventCount) // console.log('itemEventCount=', itemEventCount)
// console.log('subitemEventCount=', subitemEventCount) // console.log('subitemEventCount=', subitemEventCount)
should(itemEventCount).equal(296); should(itemEventCount).equal(296)
should(subitemEventCount).equal(600); should(subitemEventCount).equal(600)
should(dataEventCount).equal(896); should(dataEventCount).equal(896)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a xml simple file in which nodes contain text values randomly.", (done) => { it('should properly parse a xml simple file in which nodes contain text values randomly.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/randomText.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/randomText.xml')
const parser = new XmlParser(); const parser = new XmlParser()
const expectedData = const expectedData = [
[ { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: '2' }, _: 'two' },
{ $: { sub: "2" }, _: "two" }, {
{ $: { id: '1', test: 'hello' },
$: { id: "1", test: "hello" }, _: "item one two", _: 'item one two',
subitem: [{ $: { sub: "TESTING SUB" }, _: "one" }, subitem: [
{ $: { sub: "2" }, _: "two" }] { $: { sub: 'TESTING SUB' }, _: 'one' },
}, { $: { sub: '2' }, _: 'two' }
{ _: "three" }, ]
{ _: "four" }, },
{ _: "five" }, { _: 'three' },
{ { _: 'four' },
$: { id: "2" }, _: "item one two three four", { _: 'five' },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] {
} $: { id: '2' },
]; _: 'item one two three four',
subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
}
]
const expectedItems = [ const expectedItems = [
{ {
$: { id: "1", test: "hello" }, _: "item one two", $: { id: '1', test: 'hello' },
subitem: _: 'item one two',
[{ $: { sub: "TESTING SUB" }, _: "one" }, subitem: [
{ $: { sub: "2" }, _: "two" }] { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: '2' }, _: 'two' }
]
}, },
{ {
$: { id: "2" }, _: "item one two three four", $: { id: '2' },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] _: 'item one two three four',
}]; subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
const actualItems: string[] = []; }
const actualSubitems: string[] = []; ]
const actualItems: string[] = []
const actualSubitems: string[] = []
const expectedSubitems = [ const expectedSubitems = [
{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "2" }, _: "two" }, { $: { sub: '2' }, _: 'two' },
{ _: "three" }, { _: 'three' },
{ _: "four" }, { _: 'four' },
{ _: "five" } { _: 'five' }
]; ]
const actualData: string[] = []; const actualData: string[] = []
let dataEventCount = 0; let dataEventCount = 0
let itemEventCount = 0; let itemEventCount = 0
let subitemEventCount = 0; let subitemEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
itemEventCount++; itemEventCount++
actualItems.push(item); actualItems.push(item)
}); })
parser.on("subitem", (subitem) => { parser.on('subitem', (subitem) => {
subitemEventCount++; subitemEventCount++
actualSubitems.push(subitem); actualSubitems.push(subitem)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1)) // console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemEventCount) // console.log('itemEventCount=', itemEventCount)
// console.log('subitemEventCount=', subitemEventCount) // console.log('subitemEventCount=', subitemEventCount)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
should(actualItems).deepEqual(expectedItems); should(actualItems).deepEqual(expectedItems)
should(actualSubitems).deepEqual(expectedSubitems); should(actualSubitems).deepEqual(expectedSubitems)
should(dataEventCount).equal(7); should(dataEventCount).equal(7)
should(itemEventCount).equal(2); should(itemEventCount).equal(2)
should(subitemEventCount).equal(5); should(subitemEventCount).equal(5)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a huge file.", (done) => { it('should properly parse a huge file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/hugeFile.xml')
const parser = new XmlParser(); const parser = new XmlParser()
let dataEventCount = 0; let dataEventCount = 0
let itemEventCount = 0; let itemEventCount = 0
let subitemEventCount = 0; let subitemEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
itemEventCount++; itemEventCount++
}); })
parser.on("subitem", (subitem) => { parser.on('subitem', (subitem) => {
subitemEventCount++; subitemEventCount++
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemEventCount) // console.log('itemEventCount=', itemEventCount)
// console.log('subitemEventCount=', subitemEventCount) // console.log('subitemEventCount=', subitemEventCount)
should(dataEventCount).equal(6272); should(dataEventCount).equal(6272)
should(itemEventCount).equal(2072); should(itemEventCount).equal(2072)
should(subitemEventCount).equal(4200); should(subitemEventCount).equal(4200)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a simple file and return when root element when listening on it.", (done) => { it('should properly parse a simple file and return when root element when listening on it.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
const parser = new XmlParser(); const parser = new XmlParser()
const expectedData = const expectedData = [
[{ {
item: [{ item: [
$: { id: "1", test: "hello" }, {
subitem: [{ $: { sub: "TESTING SUB" }, _: "one" }, $: { id: '1', test: 'hello' },
{ $: { sub: "2" }, _: "two" }] subitem: [
}, { $: { sub: 'TESTING SUB' }, _: 'one' },
{ { $: { sub: '2' }, _: 'two' }
$: { id: "2" }, subitem: [{ _: "three" }, { _: "four" }, ]
{ _: "five" }] },
}] {
}]; $: { id: '2' },
subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
}
]
}
]
const actualData: string[] = []; const actualData: string[] = []
let dataEventCount = 0; let dataEventCount = 0
let itemsEventCount = 0; let itemsEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
should(err).not.be.ok(); should(err).not.be.ok()
done(err); done(err)
}); })
parser.on("items", (item) => { parser.on('items', (item) => {
itemsEventCount++; itemsEventCount++
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1)) // console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemsEventCount) // console.log('itemEventCount=', itemsEventCount)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
should(itemsEventCount).equal(1); should(itemsEventCount).equal(1)
should(dataEventCount).equal(1); should(dataEventCount).equal(1)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })

View File

@@ -1,117 +1,123 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("should respect the options passed", () => { describe('should respect the options passed', () => {
it("should properly generate objects with $ as key for attrs and _ as key for text value of node.", (done) => { it('should properly generate objects with $ as key for attrs and _ as key for text value of node.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
const expectedData = [ const expectedData = [
{ {
$: { id: "1", test: "hello" }, $: { id: '1', test: 'hello' },
subitem: subitem: [
[{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "2" }, _: "two" }] { $: { sub: '2' }, _: 'two' }
]
}, },
{ {
$: { id: "2" }, $: { id: '2' },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
}]; }
const actualData: string[] = []; ]
let dataEventCount = 0; const actualData: string[] = []
let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', actualData) // console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
should(dataEventCount).equal(2); should(dataEventCount).equal(2)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly generate objects with passed attrs and text keys in the options.", (done) => { it('should properly generate objects with passed attrs and text keys in the options.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
const parser = new XmlParser({ resourcePath: "/items/item", attrsKey: "attrs", textKey: "text" }); const parser = new XmlParser({ resourcePath: '/items/item', attrsKey: 'attrs', textKey: 'text' })
const expectedData = [ const expectedData = [
{ {
attrs: { id: "1", test: "hello" }, attrs: { id: '1', test: 'hello' },
subitem: subitem: [
[{ attrs: { sub: "TESTING SUB" }, text: "one" }, { attrs: { sub: 'TESTING SUB' }, text: 'one' },
{ attrs: { sub: "2" }, text: "two" }] { attrs: { sub: '2' }, text: 'two' }
]
}, },
{ {
attrs: { id: "2" }, attrs: { id: '2' },
subitem: [{ text: "three" }, { text: "four" }, { text: "five" }] subitem: [{ text: 'three' }, { text: 'four' }, { text: 'five' }]
}]; }
const actualData: string[] = []; ]
let dataEventCount = 0; const actualData: string[] = []
let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1)) // console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
should(dataEventCount).equal(2); should(dataEventCount).equal(2)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly generate objects when special symbols are passed as attrs and text keys in the options.", (done) => { it('should properly generate objects when special symbols are passed as attrs and text keys in the options.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
const parser = new XmlParser({ resourcePath: "/items/item", attrsKey: "!", textKey: "%" }); const parser = new XmlParser({ resourcePath: '/items/item', attrsKey: '!', textKey: '%' })
const expectedData = [ const expectedData = [
{ {
"!": { id: "1", test: "hello" }, '!': { id: '1', test: 'hello' },
"subitem": subitem: [
[{ "!": { sub: "TESTING SUB" }, "%": "one" }, { '!': { sub: 'TESTING SUB' }, '%': 'one' },
{ "!": { sub: "2" }, "%": "two" }] { '!': { sub: '2' }, '%': 'two' }
]
}, },
{ {
"!": { id: "2" }, '!': { id: '2' },
"subitem": [{ "%": "three" }, { "%": "four" }, { "%": "five" }] subitem: [{ '%': 'three' }, { '%': 'four' }, { '%': 'five' }]
}]; }
const actualData: string[] = []; ]
let dataEventCount = 0; const actualData: string[] = []
let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', JSON.stringify(actualData, null, 1)) // console.log('actualData=', JSON.stringify(actualData, null, 1))
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
should(dataEventCount).equal(2); should(dataEventCount).equal(2)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })

View File

@@ -1,97 +1,114 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("Parse function should work properly", () => { describe('Parse function should work properly', () => {
it("should properly parse a simple file.", (done) => { it('should properly parse a simple file.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/item.xml"); const xml = fs.readFileSync('./test/TestFiles/item.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
const expectedData = [ const expectedData = [
{ {
$: { id: "1", test: "hello" }, $: { id: '1', test: 'hello' },
subitem: subitem: [
[{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "2" }, _: "two" }] { $: { sub: '2' }, _: 'two' }
]
}, },
{ {
$: { id: "2" }, $: { id: '2' },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
}]; }
]
parser.parse(xml.toString(), (err, data) => { parser.parse(xml.toString(), (err, data) => {
if (err) { done(err); } if (err) {
should(data).deepEqual(expectedData); done(err)
done(); }
}); should(data).deepEqual(expectedData)
}); done()
})
})
it("should properly parse a medium size file.", (done) => { it('should properly parse a medium size file.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/medium.xml"); const xml = fs.readFileSync('./test/TestFiles/medium.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
parser.parse(xml, (err, data) => { parser.parse(xml, (err, data) => {
if (err) { done(err); } if (err) {
should(data.length).equal(10); done(err)
done(); }
}); should(data.length).equal(10)
}); done()
})
})
it("should properly parse a file containing many nodes.", (done) => { it('should properly parse a file containing many nodes.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/manyItems.xml"); const xml = fs.readFileSync('./test/TestFiles/manyItems.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
parser.parse(xml, (err, data) => { parser.parse(xml, (err, data) => {
if (err) { done(err); } if (err) {
should(data.length).equal(296); done(err)
done(); }
}); should(data.length).equal(296)
}); done()
})
})
it("should properly parse a xml simple file in which nodes contain text values randomly.", (done) => { it('should properly parse a xml simple file in which nodes contain text values randomly.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/randomText.xml"); const xml = fs.readFileSync('./test/TestFiles/randomText.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
const expectedData = [{ const expectedData = [
$: { id: "1", test: "hello" }, _: "item one two", {
subitem: [{ $: { sub: "TESTING SUB" }, _: "one" }, $: { id: '1', test: 'hello' },
{ $: { sub: "2" }, _: "two" }] _: 'item one two',
}, subitem: [
{ { $: { sub: 'TESTING SUB' }, _: 'one' },
$: { id: "2" }, _: "item one two three four", { $: { sub: '2' }, _: 'two' }
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] ]
} },
]; {
$: { id: '2' },
_: 'item one two three four',
subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
}
]
parser.parse(xml, (err, data) => { parser.parse(xml, (err, data) => {
if (err) { done(err); } if (err) {
should(data).deepEqual(expectedData); done(err)
should(data.length).equal(2); }
done(); should(data).deepEqual(expectedData)
}); should(data.length).equal(2)
}); done()
})
})
it("should properly parse a huge file.", (done) => { it('should properly parse a huge file.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/hugeFile.xml"); const xml = fs.readFileSync('./test/TestFiles/hugeFile.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
// console.log(parser) // console.log(parser)
parser.parse(xml, (err, data) => { parser.parse(xml, (err, data) => {
if (err) { done(err); } if (err) {
should(data.length).equal(2072); done(err)
done(); }
}); should(data.length).equal(2072)
}); done()
})
})
it("should properly return error if the xml file is corrupted.", (done) => { it('should properly return error if the xml file is corrupted.', (done) => {
const xml = fs.readFileSync("./test/TestFiles/corrupted.xml"); const xml = fs.readFileSync('./test/TestFiles/corrupted.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
parser.parse(xml, (err, data) => { parser.parse(xml, (err, data) => {
// console.log(err) // console.log(err)
should(err.message).equal("mismatched tag at line no: 12"); should(err.message).equal('mismatched tag at line no: 12')
should(data).not.be.ok(); should(data).not.be.ok()
done(); done()
}); })
}); })
}); })

View File

@@ -1,82 +1,84 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("pause and resume", () => { describe('pause and resume', () => {
it("should properly parse a simple file.", function(done) { it('should properly parse a simple file.', function (done) {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
const expectedData = [ const expectedData = [
{ {
$: { id: "1", test: "hello" }, $: { id: '1', test: 'hello' },
subitem: subitem: [
[{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "2" }, _: "two" }] { $: { sub: '2' }, _: 'two' }
]
}, },
{ {
$: { id: "2" }, $: { id: '2' },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
}]; }
const actualData: string[] = []; ]
let dataEventCount = 0; const actualData: string[] = []
let isSetTimeoutHappened = true; let dataEventCount = 0
this.timeout(4000); let isSetTimeoutHappened = true
parser.on("data", (data) => { this.timeout(4000)
actualData.push(data); parser.on('data', (data) => {
parser.pause(); actualData.push(data)
should(isSetTimeoutHappened).equal(true); parser.pause()
should(isSetTimeoutHappened).equal(true)
setTimeout(() => { setTimeout(() => {
parser.resume(); parser.resume()
isSetTimeoutHappened = true; isSetTimeoutHappened = true
}, 1000); }, 1000)
dataEventCount++; dataEventCount++
isSetTimeoutHappened = false; isSetTimeoutHappened = false
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', actualData) // console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
should(dataEventCount).equal(2); should(dataEventCount).equal(2)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should emit data events with 1sec interval between each using pause and resume.", function(done) { it('should emit data events with 1sec interval between each using pause and resume.', function (done) {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
let dataEventCount = 0; let dataEventCount = 0
let isSetTimeoutHappened = true; let isSetTimeoutHappened = true
this.timeout(20000); this.timeout(20000)
parser.on("data", (data) => { parser.on('data', (data) => {
parser.pause(); parser.pause()
should(isSetTimeoutHappened).equal(true); should(isSetTimeoutHappened).equal(true)
setTimeout(() => { setTimeout(() => {
parser.resume(); parser.resume()
isSetTimeoutHappened = true; isSetTimeoutHappened = true
}, 1000); }, 1000)
dataEventCount++; dataEventCount++
isSetTimeoutHappened = false; isSetTimeoutHappened = false
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(10); should(dataEventCount).equal(10)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })

View File

@@ -1,103 +1,102 @@
import fs from 'fs'
import 'mocha'
import should from 'should'
import stream from 'stream'
import zlib from 'zlib'
import fs from "fs"; import { XmlParser } from '../src/parser'
import "mocha"; describe.skip('performance testing', () => {
import should from "should"; it('should properly parse more than 500 MB of file.', function (done) {
import stream from "stream"; const parser = new XmlParser({ resourcePath: '/items/item' })
import zlib from "zlib";
import { XmlParser } from "../src/parser";
describe.skip("performance testing", () => {
it("should properly parse more than 500 MB of file.", function(done) {
const parser = new XmlParser({ resourcePath: "/items/item" });
// var wsStream = fs.createWriteStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml') // var wsStream = fs.createWriteStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml')
// var rsStream = fs.createReadStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml') // var rsStream = fs.createReadStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml')
let dataEventCount = 0; let dataEventCount = 0
// var maxRSSMemoryTaken = 0 // var maxRSSMemoryTaken = 0
// var rss // var rss
const startTime = Date.now(); const startTime = Date.now()
const xmlStream = new stream.Readable(); const xmlStream = new stream.Readable()
xmlStream._read = function noop() { xmlStream._read = function noop() {
// nop // nop
}; }
let dataChunk; let dataChunk
this.timeout(900000); this.timeout(900000)
const firstChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/firstChunk.xml"); const firstChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/firstChunk.xml')
xmlStream.push(firstChunk); xmlStream.push(firstChunk)
for (let i = 0; i < 2200; i++) { for (let i = 0; i < 2200; i++) {
dataChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/repetitiveChunk.xml"); dataChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/repetitiveChunk.xml')
xmlStream.push(dataChunk); xmlStream.push(dataChunk)
} }
const endingChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/endingChunk.xml"); const endingChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/endingChunk.xml')
xmlStream.push(endingChunk); xmlStream.push(endingChunk)
xmlStream.push(null); xmlStream.push(null)
parser.on("data", (data) => { parser.on('data', (data) => {
// rss = process.memoryUsage().rss // rss = process.memoryUsage().rss
// if (rss > maxRSSMemoryTaken) maxRSSMemoryTaken = rss // if (rss > maxRSSMemoryTaken) maxRSSMemoryTaken = rss
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
should(err).not.be.ok(); should(err).not.be.ok()
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
// console.log('RSS memory=', rss) // console.log('RSS memory=', rss)
const TimeTaken = Date.now() - startTime; const TimeTaken = Date.now() - startTime
// console.log('time taken=', TimeTaken) // console.log('time taken=', TimeTaken)
should(TimeTaken).be.belowOrEqual(300000); should(TimeTaken).be.belowOrEqual(300000)
should(dataEventCount).equal(4558400); should(dataEventCount).equal(4558400)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse more than 1 GB of file.", function(done) { it('should properly parse more than 1 GB of file.', function (done) {
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
// var wsStream = fs.createWriteStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml') // var wsStream = fs.createWriteStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml')
// var rsStream = fs.createReadStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml') // var rsStream = fs.createReadStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml')
let dataEventCount = 0; let dataEventCount = 0
// var maxRSSMemoryTaken = 0 // var maxRSSMemoryTaken = 0
// var rss // var rss
const startTime = Date.now(); const startTime = Date.now()
const xmlStream = new stream.Readable(); const xmlStream = new stream.Readable()
xmlStream._read = function noop() { xmlStream._read = function noop() {
// nop // nop
}; }
let dataChunk; let dataChunk
this.timeout(900000); this.timeout(900000)
const firstChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/firstChunk.xml"); const firstChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/firstChunk.xml')
xmlStream.push(firstChunk); xmlStream.push(firstChunk)
for (let i = 0; i < 4400; i++) { for (let i = 0; i < 4400; i++) {
dataChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/repetitiveChunk.xml"); dataChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/repetitiveChunk.xml')
xmlStream.push(dataChunk); xmlStream.push(dataChunk)
} }
const endingChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/endingChunk.xml"); const endingChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/endingChunk.xml')
xmlStream.push(endingChunk); xmlStream.push(endingChunk)
xmlStream.push(null); xmlStream.push(null)
parser.on("data", (data) => { parser.on('data', (data) => {
// rss = process.memoryUsage().rss // rss = process.memoryUsage().rss
// if (rss > maxRSSMemoryTaken) maxRSSMemoryTaken = rss // if (rss > maxRSSMemoryTaken) maxRSSMemoryTaken = rss
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
should(err).not.be.ok(); should(err).not.be.ok()
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
// console.log('RSS memory=', rss) // console.log('RSS memory=', rss)
const TimeTaken = Date.now() - startTime; const TimeTaken = Date.now() - startTime
// console.log('time taken=', TimeTaken) // console.log('time taken=', TimeTaken)
should(TimeTaken).be.belowOrEqual(700000); should(TimeTaken).be.belowOrEqual(700000)
should(dataEventCount).equal(9116800); should(dataEventCount).equal(9116800)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })

View File

@@ -1,107 +1,117 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("read method", () => { describe('read method', () => {
it("should properly parse a simple file.", (done) => { it('should properly parse a simple file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
const expectedData = [ const expectedData = [
{ {
$: { id: "1", test: "hello" }, $: { id: '1', test: 'hello' },
subitem: subitem: [
[{ $: { sub: "TESTING SUB" }, _: "one" }, { $: { sub: 'TESTING SUB' }, _: 'one' },
{ $: { sub: "2" }, _: "two" }] { $: { sub: '2' }, _: 'two' }
]
}, },
{ {
$: { id: "2" }, $: { id: '2' },
subitem: [{ _: "three" }, { _: "four" }, { _: "five" }] subitem: [{ _: 'three' }, { _: 'four' }, { _: 'five' }]
}]; }
const actualData: string[] = []; ]
let obj; const actualData: string[] = []
let Timeout: any; let obj
let Timeout: any
parser.on("readable", () => { parser.on('readable', () => {
Timeout = setInterval(() => { Timeout = setInterval(() => {
obj = parser.read(); obj = parser.read()
if (obj) { if (obj) {
actualData.push(obj); actualData.push(obj)
} }
obj = null; obj = null
}, 50); }, 50)
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', actualData) // console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
clearInterval(Timeout); clearInterval(Timeout)
should(actualData).deepEqual(expectedData); should(actualData).deepEqual(expectedData)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a file containing many nodes.", (done) => { it('should properly parse a file containing many nodes.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
let objCount = 0; let objCount = 0
let endEventOcurred = false; let endEventOcurred = false
parser.on("readable", () => { parser.on('readable', () => {
read(); read()
}); })
function read() { function read() {
while (parser.read()) { objCount++; } while (parser.read()) {
if (!endEventOcurred) { setTimeout(read, 50); } objCount++
}
if (!endEventOcurred) {
setTimeout(read, 50)
}
} }
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
endEventOcurred = true; endEventOcurred = true
// console.log(objCount) // console.log(objCount)
should(objCount).deepEqual(296); should(objCount).deepEqual(296)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a huge.", (done) => { it('should properly parse a huge.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/hugeFile.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
let objCount = 0; let objCount = 0
let endEventOcurred = false; let endEventOcurred = false
parser.on("readable", () => { parser.on('readable', () => {
read(); read()
}); })
function read() { function read() {
while (parser.read()) { objCount++; } while (parser.read()) {
if (!endEventOcurred) { setTimeout(read, 50); } objCount++
}
if (!endEventOcurred) {
setTimeout(read, 50)
}
} }
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
endEventOcurred = true; endEventOcurred = true
// console.log(objCount); // console.log(objCount);
should(objCount).deepEqual(2072); should(objCount).deepEqual(2072)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })

View File

@@ -1,111 +1,111 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("nodes with same names", () => { describe('nodes with same names', () => {
it("should properly parse a simple file containing nodes with same names.", (done) => { it('should properly parse a simple file containing nodes with same names.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/nodesWithSameNames.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/nodesWithSameNames.xml')
const parser = new XmlParser(); const parser = new XmlParser()
const actualData: string[] = []; const actualData: string[] = []
const actualItems: string[] = []; const actualItems: string[] = []
let dataEventCount = 0; let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
should(err).not.be.ok(); should(err).not.be.ok()
done(err); done(err)
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
actualItems.push(item); actualItems.push(item)
}); })
parser.on("end", () => { parser.on('end', () => {
should(actualItems.length).equal(18); should(actualItems.length).equal(18)
should(dataEventCount).equal(18); should(dataEventCount).equal(18)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a simple file containing nodes with same names and emit events on multiple nodes.", (done) => { it('should properly parse a simple file containing nodes with same names and emit events on multiple nodes.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/nodesWithSameNames.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/nodesWithSameNames.xml')
const parser = new XmlParser(); const parser = new XmlParser()
let dataEventCount = 0; let dataEventCount = 0
let itemEventCount = 0; let itemEventCount = 0
let subitemEventCount = 0; let subitemEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
should(err).not.be.ok(); should(err).not.be.ok()
done(err); done(err)
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
itemEventCount++; itemEventCount++
}); })
parser.on("subitem", (subitem) => { parser.on('subitem', (subitem) => {
subitemEventCount++; subitemEventCount++
}); })
parser.on("end", () => { parser.on('end', () => {
should(itemEventCount).equal(18); should(itemEventCount).equal(18)
should(subitemEventCount).equal(13); should(subitemEventCount).equal(13)
should(dataEventCount).equal(31); should(dataEventCount).equal(31)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a medium size file with same names randomly.", (done) => { it('should properly parse a medium size file with same names randomly.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/nodesWithSameNamesRandomly.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/nodesWithSameNamesRandomly.xml')
const parser = new XmlParser(); const parser = new XmlParser()
let dataEventCount = 0; let dataEventCount = 0
let itemEventCount = 0; let itemEventCount = 0
let subitemEventCount = 0; let subitemEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
itemEventCount++; itemEventCount++
}); })
parser.on("subitem", (subitem) => { parser.on('subitem', (subitem) => {
subitemEventCount++; subitemEventCount++
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
// console.log('itemEventCount=', itemEventCount) // console.log('itemEventCount=', itemEventCount)
// console.log('subitemEventCount=', subitemEventCount) // console.log('subitemEventCount=', subitemEventCount)
should(dataEventCount).equal(32); should(dataEventCount).equal(32)
should(itemEventCount).equal(19); should(itemEventCount).equal(19)
should(subitemEventCount).equal(13); should(subitemEventCount).equal(13)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })

View File

@@ -1,64 +1,64 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("should properly handle uncompressed files", () => { describe('should properly handle uncompressed files', () => {
it("should properly parse a uncompressed xml file.", (done) => { it('should properly parse a uncompressed xml file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
const gzip = zlib.createGzip(); const gzip = zlib.createGzip()
const gunzip = zlib.createGunzip(); const gunzip = zlib.createGunzip()
let dataEventCount = 0; let dataEventCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(10); should(dataEventCount).equal(10)
done(); done()
}); })
xmlStream.pipe(gzip).pipe(gunzip).pipe(parser); xmlStream.pipe(gzip).pipe(gunzip).pipe(parser)
}); })
it("should properly parse uncompressed file and go fine with pause and resume.", function(done) { it('should properly parse uncompressed file and go fine with pause and resume.', function (done) {
const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
const parser = new XmlParser({ resourcePath: "/items/item" }); const parser = new XmlParser({ resourcePath: '/items/item' })
const gzip = zlib.createGzip(); const gzip = zlib.createGzip()
const gunzip = zlib.createGunzip(); const gunzip = zlib.createGunzip()
let dataEventCount = 0; let dataEventCount = 0
let isSetTimeoutHappened = true; let isSetTimeoutHappened = true
this.timeout(20000); this.timeout(20000)
parser.on("data", (data) => { parser.on('data', (data) => {
parser.pause(); parser.pause()
should(isSetTimeoutHappened).equal(true); should(isSetTimeoutHappened).equal(true)
setTimeout(() => { setTimeout(() => {
parser.resume(); parser.resume()
isSetTimeoutHappened = true; isSetTimeoutHappened = true
}, 2000); }, 2000)
dataEventCount++; dataEventCount++
isSetTimeoutHappened = false; isSetTimeoutHappened = false
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(10); should(dataEventCount).equal(10)
done(); done()
}); })
xmlStream.pipe(gzip).pipe(gunzip).pipe(parser); xmlStream.pipe(gzip).pipe(gunzip).pipe(parser)
}); })
}); })

View File

@@ -1,99 +1,99 @@
import fs from "fs"; import fs from 'fs'
import "mocha"; import 'mocha'
import should from "should"; import should from 'should'
import stream from "stream"; import stream from 'stream'
import zlib from "zlib"; import zlib from 'zlib'
import { XmlParser } from "../src/parser"; import { XmlParser } from '../src/parser'
describe("wrong resourcePath", () => { describe('wrong resourcePath', () => {
it("should be able to detect the wrong resourcePath at root level.", (done) => { it('should be able to detect the wrong resourcePath at root level.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/item.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
const parser = new XmlParser({ resourcePath: "/wrong/noNodes", emitOnNodeName: true }); const parser = new XmlParser({ resourcePath: '/wrong/noNodes', emitOnNodeName: true })
const actualData : string[] = []; const actualData: string[] = []
const itemData : string[] = []; const itemData: string[] = []
let dataEventCount = 0; let dataEventCount = 0
let itemCount = 0; let itemCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
actualData.push(data); actualData.push(data)
dataEventCount++; dataEventCount++
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
itemData.push(item); itemData.push(item)
itemCount++; itemCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('actualData=', actualData) // console.log('actualData=', actualData)
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(actualData.length).equal(0); should(actualData.length).equal(0)
should(dataEventCount).equal(0); should(dataEventCount).equal(0)
should(itemData.length).equal(0); should(itemData.length).equal(0)
should(itemCount).equal(0); should(itemCount).equal(0)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should be able to detect wrong resourcePath while parsing xml", (done) => { it('should be able to detect wrong resourcePath while parsing xml', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
const parser = new XmlParser({ resourcePath: "/wrong/noNodes", emitOnNodeName: true }); const parser = new XmlParser({ resourcePath: '/wrong/noNodes', emitOnNodeName: true })
let dataEventCount = 0; let dataEventCount = 0
let itemCount = 0; let itemCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("item", (data) => { parser.on('item', (data) => {
itemCount++; itemCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(0); should(dataEventCount).equal(0)
should(itemCount).equal(0); should(itemCount).equal(0)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
it("should properly parse a huge file.", (done) => { it('should properly parse a huge file.', (done) => {
const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml"); const xmlStream = fs.createReadStream('./test/TestFiles/hugeFile.xml')
const parser = new XmlParser({ resourcePath: "/wrong/path", emitOnNodeName: true }); const parser = new XmlParser({ resourcePath: '/wrong/path', emitOnNodeName: true })
let dataEventCount = 0; let dataEventCount = 0
let itemCount = 0; let itemCount = 0
parser.on("data", (data) => { parser.on('data', (data) => {
dataEventCount++; dataEventCount++
}); })
parser.on("item", (item) => { parser.on('item', (item) => {
itemCount++; itemCount++
}); })
parser.on("error", (err) => { parser.on('error', (err) => {
done(err); done(err)
}); })
parser.on("end", () => { parser.on('end', () => {
// console.log('dataEventCount=', dataEventCount) // console.log('dataEventCount=', dataEventCount)
should(dataEventCount).equal(0); should(dataEventCount).equal(0)
should(itemCount).equal(0); should(itemCount).equal(0)
done(); done()
}); })
xmlStream.pipe(parser); xmlStream.pipe(parser)
}); })
}); })