diff --git a/.gitignore b/.gitignore
index 5148e52..fa962f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,4 @@ jspm_packages
# Optional REPL history
.node_repl_history
+/dist
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..da0bf16
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,51 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+
+ {
+ "type": "node",
+ "request": "launch",
+ "name": "Launch Program",
+ "program": "${workspaceFolder}\\node_stream_zip.js",
+ "preLaunchTask": "tsc: build - tsconfig.json",
+ "outFiles": [
+ "${workspaceFolder}/dist/**/*.js"
+ ]
+ },
+ {
+ "type": "node",
+ "request": "launch",
+ "name": "Mocha All",
+ "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
+ "args": [
+ "--timeout",
+ "999999",
+ "--colors",
+ "-r",
+ "ts-node/register",
+ "test/**/*.spec.ts"
+ ],
+ "console": "integratedTerminal",
+ "internalConsoleOptions": "neverOpen"
+ },
+ {
+ "type": "node",
+ "request": "launch",
+ "name": "Mocha Current File",
+ "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
+ "args": [
+ "--timeout",
+ "999999",
+ "--colors",
+ "-r",
+ "ts-node/register",
+ "${file}"
+ ],
+ "console": "integratedTerminal",
+ "internalConsoleOptions": "neverOpen"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..7f720b1
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "cSpell.words": [
+
+ ]
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..573caf5
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,614 @@
+{
+ "name": "xml-streamer",
+ "version": "0.2.1",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
+ "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.0.0"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
+ "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.0",
+ "esutils": "^2.0.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@types/lodash": {
+ "version": "4.14.132",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.132.tgz",
+ "integrity": "sha512-RNUU1rrh85NgUJcjOOr96YXr+RHwInGbaQCZmlitqOaCKXffj8bh+Zxwuq5rjDy5OgzFldDVoqk4pyLEDiwxIw==",
+ "dev": true
+ },
+ "@types/mocha": {
+ "version": "5.2.7",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
+ "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.4.tgz",
+ "integrity": "sha512-j8YL2C0fXq7IONwl/Ud5Kt0PeXw22zGERt+HSSnwbKOJVsAGkEz3sFCYwaF9IOuoG1HOtE0vKCj6sXF7Q0+Vaw==",
+ "dev": true
+ },
+ "@types/should": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@types/should/-/should-13.0.0.tgz",
+ "integrity": "sha512-Mi6YZ2ABnnGGFMuiBDP0a8s1ZDCDNHqP97UH8TyDmCWuGGavpsFMfJnAMYaaqmDlSCOCNbVLHBrSDEOpx/oLhw==",
+ "dev": true,
+ "requires": {
+ "should": "*"
+ }
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "arg": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz",
+ "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "requires": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "commander": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.0.0.tgz",
+ "integrity": "sha1-ib2d9nMrUSVrxnBTQrugLtEhMe8=",
+ "dev": true,
+ "requires": {
+ "ms": "0.6.2"
+ }
+ },
+ "diff": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
+ },
+ "file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
+ "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz",
+ "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=",
+ "dev": true
+ },
+ "growl": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz",
+ "integrity": "sha1-Sy3sjZB+k9szZiTc7AGDUC+MlCg=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "jade": {
+ "version": "0.26.3",
+ "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
+ "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=",
+ "dev": true,
+ "requires": {
+ "commander": "0.6.1",
+ "mkdirp": "0.3.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz",
+ "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
+ "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=",
+ "dev": true
+ }
+ }
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.5",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
+ "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw=="
+ },
+ "lru-cache": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
+ "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=",
+ "dev": true
+ },
+ "make-error": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
+ "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "mocha": {
+ "version": "1.21.5",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.5.tgz",
+ "integrity": "sha1-fFiwkXTfl25DSiOx6NY5hz/FKek=",
+ "dev": true,
+ "requires": {
+ "commander": "2.3.0",
+ "debug": "2.0.0",
+ "diff": "1.0.8",
+ "escape-string-regexp": "1.0.2",
+ "glob": "3.2.3",
+ "growl": "1.8.1",
+ "jade": "0.26.3",
+ "mkdirp": "0.5.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz",
+ "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=",
+ "dev": true
+ },
+ "diff": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.8.tgz",
+ "integrity": "sha1-NDJ2MI7Jkbe8giZ+1VvBQR+XFmY=",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz",
+ "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=",
+ "dev": true
+ },
+ "glob": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz",
+ "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "~2.0.0",
+ "inherits": "2",
+ "minimatch": "~0.2.11"
+ }
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
+ "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ }
+ }
+ },
+ "ms": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz",
+ "integrity": "sha1-2JwhJMb9wTU9Zai3e/GqxLGTcIw=",
+ "dev": true
+ },
+ "nan": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
+ },
+ "node-expat": {
+ "version": "2.3.15",
+ "resolved": "https://registry.npmjs.org/node-expat/-/node-expat-2.3.15.tgz",
+ "integrity": "sha1-lqU6C8AGSdr3j0/AdKI+ZEwiO0w=",
+ "requires": {
+ "bindings": "^1.2.1",
+ "nan": "^2.3.5"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz",
+ "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "semver": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
+ "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
+ "dev": true
+ },
+ "should": {
+ "version": "13.2.3",
+ "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz",
+ "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==",
+ "dev": true,
+ "requires": {
+ "should-equal": "^2.0.0",
+ "should-format": "^3.0.3",
+ "should-type": "^1.4.0",
+ "should-type-adaptors": "^1.0.1",
+ "should-util": "^1.0.0"
+ }
+ },
+ "should-equal": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz",
+ "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==",
+ "dev": true,
+ "requires": {
+ "should-type": "^1.4.0"
+ }
+ },
+ "should-format": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz",
+ "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=",
+ "dev": true,
+ "requires": {
+ "should-type": "^1.3.0",
+ "should-type-adaptors": "^1.0.1"
+ }
+ },
+ "should-type": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz",
+ "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=",
+ "dev": true
+ },
+ "should-type-adaptors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz",
+ "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==",
+ "dev": true,
+ "requires": {
+ "should-type": "^1.3.0",
+ "should-util": "^1.0.0"
+ }
+ },
+ "should-util": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz",
+ "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=",
+ "dev": true
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
+ "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.12",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz",
+ "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "ts-node": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.2.0.tgz",
+ "integrity": "sha512-m8XQwUurkbYqXrKqr3WHCW310utRNvV5OnRVeISeea7LoCWVcdfeB/Ntl8JYWFh+WRoUAdBgESrzKochQt7sMw==",
+ "dev": true,
+ "requires": {
+ "arg": "^4.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "source-map-support": "^0.5.6",
+ "yn": "^3.0.0"
+ },
+ "dependencies": {
+ "diff": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
+ "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==",
+ "dev": true
+ }
+ }
+ },
+ "tslib": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
+ "dev": true
+ },
+ "tslint": {
+ "version": "5.17.0",
+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.17.0.tgz",
+ "integrity": "sha512-pflx87WfVoYepTet3xLfDOLDm9Jqi61UXIKePOuca0qoAZyrGWonDG9VTbji58Fy+8gciUn8Bt7y69+KEVjc/w==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "builtin-modules": "^1.1.1",
+ "chalk": "^2.3.0",
+ "commander": "^2.12.1",
+ "diff": "^3.2.0",
+ "glob": "^7.1.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "resolve": "^1.3.2",
+ "semver": "^5.3.0",
+ "tslib": "^1.8.0",
+ "tsutils": "^2.29.0"
+ }
+ },
+ "tsutils": {
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ },
+ "typescript": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.1.tgz",
+ "integrity": "sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "yn": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.0.tgz",
+ "integrity": "sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg==",
+ "dev": true
+ }
+ }
+}
diff --git a/package.json b/package.json
index 0998fed..566f65f 100644
--- a/package.json
+++ b/package.json
@@ -24,19 +24,39 @@
"url": "https://github.com/Sai1919/xml-streamer"
},
"dependencies": {
- "node-expat": "2.3.15",
- "lodash": "4.17.5"
+ "lodash": "4.17.5",
+ "node-expat": "2.3.15"
},
"devDependencies": {
"mocha": "^1.21.4",
- "should": "11.1.1",
- "standard": "8.5.0"
+ "should": "^13.2.3",
+ "@types/lodash": "^4.14.132",
+ "@types/mocha": "^5.2.7",
+ "@types/node": "^12.0.4",
+ "@types/should": "^13.0.0",
+ "ts-node": "^8.2.0",
+ "tslint": "^5.17.0",
+ "typescript": "^3.5.1"
},
"optionalDependencies": {},
- "main": "./parser",
+ "main": "dist/parser.js",
+ "types": "dist/parser.d.ts",
"scripts": {
- "test": "mocha && standard"
+ "performance-test": "node --prof node_modules/mocha/bin/_mocha -r ts-node/register test/**/*.spec.ts",
+ "test-one": "mocha -r ts-node/register",
+ "test": "mocha -r ts-node/register test/**/*.spec.ts",
+ "lint": "tslint --project .",
+ "dryrun": "tsc -noEmit",
+ "build": "tsc",
+ "prepublish": "npm run lint && npm run dryrun && npm run test",
+ "install": "npm run build"
},
+ "contributors": [
+ {
+ "name": "Dror Gluska",
+ "email": "drorgl@gmail.com"
+ }
+ ],
"maintainers": [
{
"name": "Sai Teja",
diff --git a/parser.js b/parser.js
deleted file mode 100644
index 302248f..0000000
--- a/parser.js
+++ /dev/null
@@ -1,281 +0,0 @@
-var expat = require('node-expat')
-var _ = require('lodash')
-var util = require('util')
-var stream = require('stream')
-
-var ParserState = require('./parserState')
-var defaults = {
- resourcePath: '',
- emitOnNodeName: false,
- attrsKey: '$',
- textKey: '_',
- explicitArray: true,
- verbatimText: false
-}
-
-function XmlParser (opts) {
- this.opts = _.defaults(opts, defaults)
- this.parserState = new ParserState()
- this.parser = new expat.Parser('UTF-8')
- stream.Transform.call(this)
- this._readableState.objectMode = true
-}
-util.inherits(XmlParser, stream.Transform)
-
-XmlParser.prototype.checkForInterestedNodeListeners = function () {
- var ignore = [ 'end', 'prefinish', 'data', 'error' ]
- var eventNames = Object.keys(this._events)
-
- for (var i = 0; i < eventNames.length; i++) {
- if (_.includes(ignore, eventNames[i], 0)) continue
- this.parserState.interestedNodes.push(eventNames[i])
- }
-}
-
-XmlParser.prototype._transform = function (chunk, encoding, callback) {
- if (encoding !== 'buffer') this.emit('error', new Error('unsupported encoding'))
-
- this.processChunk(chunk)
- callback()
-}
-
-XmlParser.prototype.processChunk = function (chunk) {
- var parser = this.parser
- var state = this.parserState
-
- if (state.isRootNode) {
- this.checkForInterestedNodeListeners()
- registerEvents.call(this)
- }
-
- if (typeof chunk === 'string') {
- if (!parser.parse('', true)) processError.call(this)
- } else {
- if (!parser.parse(chunk.toString())) processError.call(this)
- }
-}
-
-XmlParser.prototype.parse = function (chunk, cb) {
- var parser = this.parser
- var state = this.parserState
- var error
-
- if (state.isRootNode) {
- this.checkForInterestedNodeListeners()
- registerEvents.call(this)
- }
-
- if (typeof chunk === Buffer) chunk = chunk.toString()
-
- this.on('error', function (err) {
- error = err
- })
-
- if (!parser.parse(chunk)) {
- error = processError.call(this)
- }
-
- if (error) return cb(error)
-
- return cb(null, this._readableState.buffer)
-}
-
-function registerEvents () {
- var scope = this
- var parser = this.parser
- var state = this.parserState
- var lastIndex
- var resourcePath = this.opts.resourcePath
- var attrsKey = this.opts.attrsKey
- var textKey = this.opts.textKey
- var interestedNodes = state.interestedNodes
- var explicitArray = this.opts.explicitArray
- var verbatimText = this.opts.verbatimText;
-
- parser.on('startElement', function (name, attrs) {
- if (state.isRootNode) state.isRootNode = false;
- state.currentPath = state.currentPath + '/' + name
- checkForResourcePath(name)
- if (state.isPathfound) processStartElement(name, attrs)
- })
-
- parser.on('endElement', function (name) {
- state.lastEndedNode = name
- lastIndex = state.currentPath.lastIndexOf('/' + name)
- state.currentPath = state.currentPath.substring(0, lastIndex)
- if (state.isPathfound) processEndElement(name)
- checkForResourcePath(name)
- })
-
- parser.on('text', function (text) {
- if (state.isPathfound) processText(text)
- })
-
- parser.on('error', function (err) {
- processError.call(this, err)
- })
-
- function processStartElement (name, attrs) {
- if (!name) return
-
- var obj = {}
- if (attrs && !_.isEmpty(attrs)) obj[attrsKey] = attrs
- var tempObj = state.object
- var path = getRelativePath(name)
- if (!path) {
- if (attrs && !_.isEmpty(attrs)) state.object[attrsKey] = attrs
- return
- }
- var tokens = path.split('.')
-
- for (var i = 0; i < tokens.length; i++) {
- if (tempObj[tokens[i]] && !(explicitArray === false && i === tokens.length - 1)) {
- tempObj = tempObj[tokens[i]]
- } else {
- // if explicitArray is true then create each node as array
- // irrespective of how many nodes are there with same name.
- tempObj[tokens[i]] = explicitArray ? [] : obj
- tempObj = tempObj[tokens[i]]
- }
- if (Array.isArray(tempObj) && i !== tokens.length - 1) tempObj = tempObj[tempObj.length - 1]
- }
-
- if (Array.isArray(tempObj)) {
- tempObj.push(obj)
- }
- }
-
- function processEndElement (name) {
- if (resourcePath) {
- var index = resourcePath.lastIndexOf('/')
- var rpath = resourcePath.substring(0, index)
-
- if (rpath === state.currentPath) {
- scope.push(state.object)
- if (scope.opts.emitOnNodeName) scope.emit(name, state.object)
- state.object = {}
- }
- } else {
- if (_.includes(interestedNodes, name, 0)) {
- emitInterestedNode(name)
- if (state.firstFoundNode === name) {
- state.object = {}
- state.firstFoundNode = ''
- state.isPathfound = false
- }
- }
- }
- }
-
- function emitInterestedNode (name) {
- var index
- var xpath
- var pathTokens
-
- xpath = state.currentPath.substring(1)
- pathTokens = xpath.split('/')
- pathTokens.push(name)
- index = pathTokens.indexOf(state.firstFoundNode)
- pathTokens = _.drop(pathTokens, index + 1)
- var tempObj = state.object
- for (var i = 0; i < pathTokens.length; i++) {
- tempObj = tempObj[pathTokens[i]]
- }
- if (Array.isArray(tempObj)) tempObj = tempObj[tempObj.length - 1]
- scope.emit(name, tempObj)
- scope.push(tempObj)
- }
-
- function processText (text) {
- if ((!text) || ((!verbatimText) && !/\S/.test(text))) {
- return
- }
- var path = getRelativePath()
- var tempObj = state.object
- if (!path) {
- if (!state.object[textKey]) state.object[textKey] = ''
- state.object[textKey] = state.object[textKey] + text
- return
- }
- var tokens = path.split('.')
- for (var i = 0; i < tokens.length; i++) {
- if (tempObj[tokens[i]]) {
- tempObj = tempObj[tokens[i]]
- } else {
- tempObj[tokens[i]] = explicitArray ? [] : {}
- tempObj = tempObj[tokens[i]]
- }
- if (Array.isArray(tempObj) && i !== tokens.length - 1) tempObj = tempObj[tempObj.length - 1]
- }
-
- if (Array.isArray(tempObj)) {
- var obj = tempObj[tempObj.length - 1]
- if (!obj[textKey]) obj[textKey] = ''
- obj[textKey] = obj[textKey] + text
- } else {
- if (!tempObj[textKey]) tempObj[textKey] = ''
- tempObj[textKey] = tempObj[textKey] + text
- }
- }
-
- function checkForResourcePath (name) {
- if (resourcePath) {
- if (state.currentPath.indexOf(resourcePath) === 0) {
- state.isPathfound = true
- } else {
- state.isPathfound = false
- }
- } else {
- if (_.includes(interestedNodes, name, 0)) {
- state.isPathfound = true
- if (!state.firstFoundNode) {
- state.firstFoundNode = name
- }
- }
- }
- }
-
- function getRelativePath () {
- var tokens
- var jsonPath
- var index
-
- if (resourcePath) {
- var xpath = state.currentPath.substring(resourcePath.length)
-
- if (!xpath) return
- if (xpath[0] === '/') xpath = xpath.substring(1)
- tokens = xpath.split('/')
- jsonPath = tokens.join('.')
- } else {
- xpath = state.currentPath.substring(1)
- tokens = xpath.split('/')
- index = tokens.indexOf(state.firstFoundNode)
- tokens = _.drop(tokens, index + 1)
- jsonPath = tokens.join('.')
- }
- return jsonPath
- }
-}
-
-function processError (err) {
- var parser = this.parser
- var error = ''
-
- if (err) {
- error = err
- } else {
- error = parser.getError()
- }
- error = new Error(error + ' at line no: ' + parser.getCurrentLineNumber())
- this.emit('error', error)
- return error
-}
-
-XmlParser.prototype._flush = function (callback) {
- this.processChunk('')
- callback()
-}
-
-module.exports = XmlParser
-
diff --git a/parserState.js b/parserState.js
deleted file mode 100644
index 2456342..0000000
--- a/parserState.js
+++ /dev/null
@@ -1,13 +0,0 @@
-
-function ParserState () {
- this.currentPath = ''
- this.lastEndedNode = ''
- this.isPathfound = false
- this.object = {}
- this.paused = false
- this.isRootNode = true
- this.firstFoundNode = ''
- this.interestedNodes = []
-}
-
-module.exports = ParserState
diff --git a/src/parser.ts b/src/parser.ts
new file mode 100644
index 0000000..ce0a709
--- /dev/null
+++ b/src/parser.ts
@@ -0,0 +1,300 @@
+///
+import _ from "lodash";
+import * as expat from "node-expat";
+import stream from "stream";
+import util from "util";
+
+import { ParserState } from "./parserState";
+const defaults = {
+ resourcePath: "",
+ emitOnNodeName: false,
+ attrsKey: "$",
+ textKey: "_",
+ explicitArray: true,
+ verbatimText: false
+};
+
+export interface IXmlParserOptions {
+ resourcePath?: string;
+ emitOnNodeName?: boolean;
+ attrsKey?: string;
+ textKey?: string;
+ explicitArray?: boolean;
+ verbatimText?: boolean;
+}
+
+export class XmlParser extends stream.Transform {
+ public parserState: ParserState;
+ private opts: IXmlParserOptions;
+ private _readableState: { objectMode: true, buffer: any };
+ private parser: expat.Parser;
+ constructor(opts?: IXmlParserOptions) {
+ super();
+ this.opts = _.defaults(opts, defaults);
+ this.parserState = new ParserState();
+ this.parser = new expat.Parser();
+ this._readableState.objectMode = true;
+ }
+
+ public checkForInterestedNodeListeners() {
+ const ignore = ["end", "prefinish", "data", "error"];
+ const eventNames = Object.keys((this as any)._events);
+
+ // tslint:disable-next-line:prefer-for-of
+ for (let i = 0; i < eventNames.length; i++) {
+ if (_.includes(ignore, eventNames[i], 0)) { continue; }
+ this.parserState.interestedNodes.push(eventNames[i]);
+ }
+ }
+
+ public _transform(chunk: Buffer | string, encoding: string, callback: () => void) {
+ if (encoding !== "buffer") { this.emit("error", new Error("unsupported encoding")); }
+
+ this.processChunk(chunk);
+ callback();
+ }
+
+ public processChunk(chunk: string | Buffer) {
+ const parser = this.parser;
+ const state = this.parserState;
+
+ if (state.isRootNode) {
+ this.checkForInterestedNodeListeners();
+ registerEvents.call(this);
+ }
+
+ if (typeof chunk === "string") {
+ if (!parser.parse("", true)) { processError.call(this); }
+ } else {
+ if (!parser.parse(chunk.toString())) { processError.call(this); }
+ }
+ }
+
+ public parse(chunk: Buffer | string, cb: (error: Error, data?: Buffer) => void) {
+ const parser = this.parser;
+ const state = this.parserState;
+ let error;
+
+ if (state.isRootNode) {
+ this.checkForInterestedNodeListeners();
+ registerEvents.call(this);
+ }
+
+ if (chunk instanceof Buffer) { chunk = chunk.toString(); }
+
+ this.on("error", (err) => {
+ error = err;
+ });
+
+ if (!parser.parse(chunk)) {
+ error = processError.call(this);
+ }
+
+ if (error) { return cb(error); }
+
+ const result = [];
+ while (this._readableState.buffer.length > 0) {
+ result.push(this._readableState.buffer.consume());
+ }
+ return cb(null, result as any);
+ }
+
+ public _flush(callback: () => void) {
+ this.processChunk("");
+ callback();
+ }
+
+}
+
+function registerEvents() {
+ const scope = this;
+ const parser: expat.Parser = this.parser;
+ const state = this.parserState;
+ let lastIndex;
+ const resourcePath = this.opts.resourcePath;
+ const attrsKey = this.opts.attrsKey;
+ const textKey = this.opts.textKey;
+ const interestedNodes = state.interestedNodes;
+ const explicitArray = this.opts.explicitArray;
+ const verbatimText = this.opts.verbatimText;
+
+ parser.on("startElement", (name, attrs) => {
+ if (state.isRootNode) { state.isRootNode = false; }
+ state.currentPath = state.currentPath + "/" + name;
+ checkForResourcePath(name);
+ if (state.isPathfound) { processStartElement(name, attrs); }
+ });
+
+ parser.on("endElement", (name) => {
+ state.lastEndedNode = name;
+ lastIndex = state.currentPath.lastIndexOf("/" + name);
+ state.currentPath = state.currentPath.substring(0, lastIndex);
+ if (state.isPathfound) { processEndElement(name); }
+ checkForResourcePath(name);
+ });
+
+ parser.on("text", (text) => {
+ if (state.isPathfound) { processText(text); }
+ });
+
+ parser.on("error", function(err) {
+ processError.call(this, err);
+ });
+
+ function processStartElement(name: string, attrs: any) {
+ 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(".");
+
+ for (let i = 0; i < tokens.length; i++) {
+ if (tempObj[tokens[i]] && !(explicitArray === false && i === tokens.length - 1)) {
+ tempObj = tempObj[tokens[i]];
+ } else {
+ // if explicitArray is true then create each node as array
+ // irrespective of how many nodes are there with same name.
+ tempObj[tokens[i]] = explicitArray ? [] : obj;
+ tempObj = tempObj[tokens[i]];
+ }
+ if (Array.isArray(tempObj) && i !== tokens.length - 1) { tempObj = tempObj[tempObj.length - 1]; }
+ }
+
+ if (Array.isArray(tempObj)) {
+ tempObj.push(obj);
+ }
+ }
+
+ function processEndElement(name: string) {
+ if (resourcePath) {
+ const index = resourcePath.lastIndexOf("/");
+ const rpath = resourcePath.substring(0, index);
+
+ if (rpath === state.currentPath) {
+ scope.push(state.object);
+ if (scope.opts.emitOnNodeName) { scope.emit(name, state.object); }
+ state.object = {};
+ }
+ } else {
+ if (_.includes(interestedNodes, name, 0)) {
+ emitInterestedNode(name);
+ if (state.firstFoundNode === name) {
+ state.object = {};
+ state.firstFoundNode = "";
+ state.isPathfound = false;
+ }
+ }
+ }
+ }
+
+ function emitInterestedNode(name: string) {
+ let index;
+ let xpath;
+ let pathTokens;
+
+ xpath = state.currentPath.substring(1);
+ pathTokens = xpath.split("/");
+ pathTokens.push(name);
+ index = pathTokens.indexOf(state.firstFoundNode);
+ pathTokens = _.drop(pathTokens, index + 1);
+ let tempObj = state.object;
+ // tslint:disable-next-line:prefer-for-of
+ for (let i = 0; i < pathTokens.length; i++) {
+ tempObj = tempObj[pathTokens[i] as any];
+ }
+ if (Array.isArray(tempObj)) { tempObj = tempObj[tempObj.length - 1]; }
+ scope.emit(name, tempObj);
+ scope.push(tempObj);
+ }
+
+ function processText(text: string) {
+ if ((!text) || ((!verbatimText) && !/\S/.test(text))) {
+ return;
+ }
+ const path = getRelativePath();
+ let tempObj = state.object;
+ if (!path) {
+ if (!state.object[textKey]) { state.object[textKey] = ""; }
+ state.object[textKey] = state.object[textKey] + text;
+ return;
+ }
+ const tokens = path.split(".");
+ for (let i = 0; i < tokens.length; i++) {
+ if (tempObj[tokens[i]]) {
+ tempObj = tempObj[tokens[i]];
+ } else {
+ tempObj[tokens[i]] = explicitArray ? [] : {};
+ tempObj = tempObj[tokens[i]];
+ }
+ if (Array.isArray(tempObj) && i !== tokens.length - 1) { tempObj = tempObj[tempObj.length - 1]; }
+ }
+
+ if (Array.isArray(tempObj)) {
+ const obj = tempObj[tempObj.length - 1];
+ if (!obj[textKey]) { obj[textKey] = ""; }
+ obj[textKey] = obj[textKey] + text;
+ } else {
+ if (!tempObj[textKey]) { tempObj[textKey] = ""; }
+ tempObj[textKey] = tempObj[textKey] + text;
+ }
+ }
+
+ function checkForResourcePath(name: string) {
+ if (resourcePath) {
+ if (state.currentPath.indexOf(resourcePath) === 0) {
+ state.isPathfound = true;
+ } else {
+ state.isPathfound = false;
+ }
+ } else {
+ if (_.includes(interestedNodes, name, 0)) {
+ state.isPathfound = true;
+ if (!state.firstFoundNode) {
+ state.firstFoundNode = name;
+ }
+ }
+ }
+ }
+
+ function getRelativePath() {
+ let tokens;
+ let jsonPath;
+ let index;
+
+ if (resourcePath) {
+ let xpath = state.currentPath.substring(resourcePath.length);
+
+ if (!xpath) { return; }
+ if (xpath[0] === "/") { xpath = xpath.substring(1); }
+ tokens = xpath.split("/");
+ jsonPath = tokens.join(".");
+ } else {
+ const xpath = state.currentPath.substring(1);
+ tokens = xpath.split("/");
+ index = tokens.indexOf(state.firstFoundNode);
+ tokens = _.drop(tokens, index + 1);
+ jsonPath = tokens.join(".");
+ }
+ return jsonPath;
+ }
+}
+
+function processError(err: Error) {
+ const parser = this.parser;
+ let error: Error = null;
+
+ if (err) {
+ error = err;
+ } else {
+ error = parser.getError();
+ }
+ error = new Error(error + " at line no: " + parser.getCurrentLineNumber());
+ this.emit("error", error);
+ return error;
+}
diff --git a/src/parserState.ts b/src/parserState.ts
new file mode 100644
index 0000000..ac4856a
--- /dev/null
+++ b/src/parserState.ts
@@ -0,0 +1,11 @@
+
+export class ParserState {
+ public currentPath = "";
+ public lastEndedNode = "";
+ public isPathfound = false;
+ public object: any = {};
+ public paused = false;
+ public isRootNode = true;
+ public firstFoundNode = "";
+ public interestedNodes: string[] = [];
+}
diff --git a/test/basic.spec.ts b/test/basic.spec.ts
new file mode 100644
index 0000000..c91ec8d
--- /dev/null
+++ b/test/basic.spec.ts
@@ -0,0 +1,146 @@
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+
+describe("Basic behavior", () => {
+ it("should properly parse a simple file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ const expectedData = [
+ {
+ $: { id: "1", test: "hello" },
+ subitem:
+ [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ {
+ $: { id: "2" },
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }];
+ const actualData: string[] = [];
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ should(err).not.be.ok();
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', actualData)
+ // console.log('dataEventCount=', dataEventCount)
+ should(actualData).deepEqual(expectedData);
+ should(dataEventCount).equal(2);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a medium size file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(10);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a file containing many nodes.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(296);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ 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 parser = new XmlParser({ resourcePath: "/items/item" });
+ const expectedData = [{
+ $: { id: "1", test: "hello" }, _: " item one two",
+ subitem: [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ {
+ $: { id: "2" }, _: " item one two three four",
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }
+ ];
+ const actualData: string[] = [];
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', JSON.stringify(actualData, null, 1))
+ // console.log('dataEventCount=', dataEventCount)
+ should(actualData).deepEqual(expectedData);
+ should(dataEventCount).equal(2);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a huge file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ // console.log(parser)
+ let dataEventCount = 0;
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(2072);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/test/cdata_and_comments.spec.ts b/test/cdata_and_comments.spec.ts
new file mode 100644
index 0000000..75499bd
--- /dev/null
+++ b/test/cdata_and_comments.spec.ts
@@ -0,0 +1,30 @@
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+describe("CData and comments in xml", () => {
+ it("should properly parse a simple file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/CData-comments.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(296);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/test/emit.spec.ts b/test/emit.spec.ts
new file mode 100644
index 0000000..ee19474
--- /dev/null
+++ b/test/emit.spec.ts
@@ -0,0 +1,136 @@
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+describe("emitOnNodeName", () => {
+ it("should properly emit events on node names.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true });
+ const expectedData = [
+ {
+ $: { id: "1", test: "hello" },
+ subitem:
+ [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ {
+ $: { id: "2" },
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }];
+ const actualData: string[] = [];
+ const itemData : string[] = [];
+ let dataEventCount = 0;
+ let itemCount = 0;
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("item", (item) => {
+ itemData.push(item);
+ itemCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', actualData)
+ // console.log('dataEventCount=', dataEventCount)
+ should(actualData).deepEqual(expectedData);
+ should(dataEventCount).equal(2);
+ should(itemData).deepEqual(expectedData);
+ should(itemCount).equal(2);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly emit events on node names while parsing a medium size file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true });
+
+ let dataEventCount = 0;
+ let itemCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("item", (data) => {
+ itemCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(10);
+ should(itemCount).equal(10);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a file containing many nodes.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true });
+
+ let dataEventCount = 0;
+ let itemCount = 0;
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("item", (data) => {
+ itemCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(296);
+ should(itemCount).equal(296);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a huge file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item", emitOnNodeName: true });
+
+ let dataEventCount = 0;
+ let itemCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("item", (item) => {
+ itemCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(2072);
+ should(itemCount).equal(2072);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/test/error.spec.ts b/test/error.spec.ts
new file mode 100644
index 0000000..effd02b
--- /dev/null
+++ b/test/error.spec.ts
@@ -0,0 +1,45 @@
+
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+describe("Error Handling", () => {
+ 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) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ // console.log(err)
+ should(err.message).equal("mismatched tag at line no: 11");
+ done();
+ });
+
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly return error if the large xml file is corrupted.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/largeCorruptedFile.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ // console.log(err)
+ should(err.message).equal("mismatched tag at line no: 8346");
+ done();
+ });
+
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/test/explicit_array.spec.ts b/test/explicit_array.spec.ts
new file mode 100644
index 0000000..df03009
--- /dev/null
+++ b/test/explicit_array.spec.ts
@@ -0,0 +1,250 @@
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+describe("should respect explicitArray constructor option", () => {
+ it("should properly parse a simple file with explicitArray set to false.", (done) => {
+ const xml = fs.readFileSync("./test/TestFiles/item.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
+ const expectedData = [
+ {
+ $: { id: "1", test: "hello" },
+ subitem: { $: { sub: "2" }, _: "two" }
+ },
+ {
+ $: { id: "2" },
+ subitem: { _: "five" }
+ }];
+
+ parser.parse(xml.toString(), (err, data) => {
+ if (err) { done(err); }
+ // console.log('data=', JSON.stringify(data))
+ should(data).deepEqual(expectedData);
+ done();
+ });
+ });
+
+ it("should properly parse a medium size file with explicitArray set to false.", (done) => {
+ const xml = fs.readFileSync("./test/TestFiles/medium.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
+ const expectedData = [
+ {
+ $: {
+ id: "1",
+ test: "hello"
+ },
+ subitem: {
+ $: {
+ sub: "2"
+ },
+ _: "two"
+ }
+ },
+ {
+ $: {
+ id: "2"
+ },
+ subitem: {
+ _: "five"
+ }
+ },
+ {
+ $: {
+ id: "3",
+ test: "hello"
+ },
+ subitem: {
+ $: {
+ sub: "2"
+ },
+ _: "two"
+ }
+ },
+ {
+ $: {
+ id: "4",
+ test: "hello"
+ },
+ subitem: {
+ $: {
+ sub: "2"
+ },
+ _: "two"
+ }
+ },
+ {
+ $: {
+ id: "5",
+ test: "hello"
+ },
+ subitem: {
+ $: {
+ sub: "2"
+ },
+ _: "two"
+ }
+ },
+ {
+ $: {
+ id: "6",
+ test: "hello"
+ },
+ subitem: {
+ $: {
+ sub: "2"
+ },
+ _: "two"
+ }
+ },
+ {
+ $: {
+ id: "7",
+ test: "hello"
+ },
+ subitem: {
+ $: {
+ sub: "2"
+ },
+ _: "two"
+ }
+ },
+ {
+ $: {
+ id: "8",
+ test: "hello"
+ },
+ subitem: {
+ $: {
+ sub: "2"
+ },
+ _: "two"
+ }
+ },
+ {
+ $: {
+ id: "9",
+ test: "hello"
+ },
+ subitem: {
+ $: {
+ sub: "2"
+ },
+ _: "two"
+ }
+ },
+ {
+ $: {
+ id: "10",
+ test: "hello"
+ },
+ subitem: {
+ $: {
+ sub: "2"
+ },
+ _: "two"
+ }
+ }
+ ];
+ parser.parse(xml, (err, data) => {
+ if (err) { done(err); }
+
+ should(data).deepEqual(expectedData);
+ should(data.length).equal(10);
+ 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 parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
+
+ parser.parse(xml, (err, data) => {
+ if (err) { done(err); }
+
+ should(data.length).equal(296);
+ 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 parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
+ const expectedData = [{
+ $: { id: "1", test: "hello" }, _: " item one two",
+ subitem: { $: { sub: "2" }, _: "two" }
+ },
+ {
+ $: { id: "2" }, _: " item one two three four",
+ subitem: { _: "five" }
+ }
+ ];
+
+ parser.parse(xml, (err, data) => {
+ if (err) { done(err); }
+
+ should(data).deepEqual(expectedData);
+ should(data.length).equal(2);
+ done();
+ });
+ });
+
+ it("should properly parse a huge file with explicitArray set to false.", (done) => {
+ const xml = fs.readFileSync("./test/TestFiles/hugeFile.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
+ // console.log(parser)
+ parser.parse(xml, (err, data) => {
+ if (err) { done(err); }
+ should(data.length).equal(2072);
+ done();
+ });
+ });
+
+ it("should properly return error if the xml file is corrupted.", (done) => {
+ const xml = fs.readFileSync("./test/TestFiles/corrupted.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item", explicitArray: false });
+
+ parser.parse(xml, (err, data) => {
+ // console.log(err)
+ should(err.message).equal("mismatched tag at line no: 11");
+ should(data).not.be.ok();
+ 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 parser = new XmlParser({ resourcePath: "/items/item", attrsKey: "!", textKey: "%", explicitArray: false });
+ const expectedData = [
+ {
+ "!": { id: "1", test: "hello" },
+ "subitem": { "!": { sub: "2" }, "%": "two" }
+ },
+ {
+ "!": { id: "2" },
+ "subitem": { "%": "five" }
+ }];
+ const actualData : string[] = [];
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', JSON.stringify(actualData, null, 1))
+ // console.log('dataEventCount=', dataEventCount)
+ should(actualData).deepEqual(expectedData);
+ should(dataEventCount).equal(2);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/test/interested.spec.ts b/test/interested.spec.ts
new file mode 100644
index 0000000..0a63b91
--- /dev/null
+++ b/test/interested.spec.ts
@@ -0,0 +1,321 @@
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+
+describe("interested Nodes", () => {
+ it("should properly parse a simple file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
+ const parser = new XmlParser();
+
+ const expectedData =
+ [
+ { $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" },
+ {
+ $: { id: "1", test: "hello" },
+ subitem: [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ { _: "three" },
+ { _: "four" },
+ { _: "five" },
+ {
+ $: { id: "2" },
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }
+ ];
+
+ const actualData: string[] = [];
+ let dataEventCount = 0;
+ const expectedItems = [
+ {
+ $: { id: "1", test: "hello" },
+ subitem:
+ [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ {
+ $: { id: "2" },
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }];
+ const actualItems: string[] = [];
+ const actualSubitems: string[] = [];
+ const expectedSubitems = [
+ { $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" },
+ { _: "three" },
+ { _: "four" },
+ { _: "five" }
+ ];
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ should(err).not.be.ok();
+ done(err);
+ });
+
+ parser.on("item", (item) => {
+ actualItems.push(item);
+ });
+
+ parser.on("subitem", (subitem) => {
+ actualSubitems.push(subitem);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', JSON.stringify(actualData, null, 1))
+ // console.log('dataEventCount=', dataEventCount)
+ should(actualData).deepEqual(expectedData);
+ should(actualItems).deepEqual(expectedItems);
+ should(actualSubitems).deepEqual(expectedSubitems);
+ should(actualSubitems.length).equal(5);
+ should(actualItems.length).equal(2);
+ should(dataEventCount).equal(7);
+ done();
+ });
+
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a medium size file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml");
+ const parser = new XmlParser();
+
+ let dataEventCount = 0;
+ let itemEventCount = 0;
+ let subitemEventCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("item", (item) => {
+ itemEventCount++;
+ });
+
+ parser.on("subitem", (subitem) => {
+ subitemEventCount++;
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ // console.log('itemEventCount=', itemEventCount)
+ // console.log('subitemEventCount=', subitemEventCount)
+ should(dataEventCount).equal(31);
+ should(itemEventCount).equal(10);
+ should(subitemEventCount).equal(21);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a file containing many nodes.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml");
+ const parser = new XmlParser();
+
+ let dataEventCount = 0;
+ let itemEventCount = 0;
+ let subitemEventCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("item", (item) => {
+ itemEventCount++;
+ });
+
+ parser.on("subitem", (subitem) => {
+ subitemEventCount++;
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ // console.log('itemEventCount=', itemEventCount)
+ // console.log('subitemEventCount=', subitemEventCount)
+ should(itemEventCount).equal(296);
+ should(subitemEventCount).equal(600);
+ should(dataEventCount).equal(896);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ 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 parser = new XmlParser();
+ const expectedData =
+ [
+ { $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" },
+ {
+ $: { id: "1", test: "hello" }, _: " item one two",
+ subitem: [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ { _: "three" },
+ { _: "four" },
+ { _: "five" },
+ {
+ $: { id: "2" }, _: " item one two three four",
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }
+ ];
+ const expectedItems = [
+ {
+ $: { id: "1", test: "hello" }, _: " item one two",
+ subitem:
+ [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ {
+ $: { id: "2" }, _: " item one two three four",
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }];
+ const actualItems: string[] = [];
+ const actualSubitems: string[] = [];
+ const expectedSubitems = [
+ { $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" },
+ { _: "three" },
+ { _: "four" },
+ { _: "five" }
+ ];
+ const actualData: string[] = [];
+ let dataEventCount = 0;
+ let itemEventCount = 0;
+ let subitemEventCount = 0;
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("item", (item) => {
+ itemEventCount++;
+ actualItems.push(item);
+ });
+
+ parser.on("subitem", (subitem) => {
+ subitemEventCount++;
+ actualSubitems.push(subitem);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', JSON.stringify(actualData, null, 1))
+ // console.log('dataEventCount=', dataEventCount)
+ // console.log('itemEventCount=', itemEventCount)
+ // console.log('subitemEventCount=', subitemEventCount)
+ should(actualData).deepEqual(expectedData);
+ should(actualItems).deepEqual(expectedItems);
+ should(actualSubitems).deepEqual(expectedSubitems);
+ should(dataEventCount).equal(7);
+ should(itemEventCount).equal(2);
+ should(subitemEventCount).equal(5);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a huge file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml");
+ const parser = new XmlParser();
+
+ let dataEventCount = 0;
+ let itemEventCount = 0;
+ let subitemEventCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("item", (item) => {
+ itemEventCount++;
+ });
+
+ parser.on("subitem", (subitem) => {
+ subitemEventCount++;
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ // console.log('itemEventCount=', itemEventCount)
+ // console.log('subitemEventCount=', subitemEventCount)
+ should(dataEventCount).equal(6272);
+ should(itemEventCount).equal(2072);
+ should(subitemEventCount).equal(4200);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ 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 parser = new XmlParser();
+ const expectedData =
+ [{
+ item: [{
+ $: { id: "1", test: "hello" },
+ subitem: [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ {
+ $: { id: "2" }, subitem: [{ _: "three" }, { _: "four" },
+ { _: "five" }]
+ }]
+ }];
+
+ const actualData: string[] = [];
+ let dataEventCount = 0;
+ let itemsEventCount = 0;
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ should(err).not.be.ok();
+ done(err);
+ });
+
+ parser.on("items", (item) => {
+ itemsEventCount++;
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', JSON.stringify(actualData, null, 1))
+ // console.log('dataEventCount=', dataEventCount)
+ // console.log('itemEventCount=', itemsEventCount)
+ should(actualData).deepEqual(expectedData);
+ should(itemsEventCount).equal(1);
+ should(dataEventCount).equal(1);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/test/options.spec.ts b/test/options.spec.ts
new file mode 100644
index 0000000..0ab70ab
--- /dev/null
+++ b/test/options.spec.ts
@@ -0,0 +1,117 @@
+import fs from "fs";
+import "mocha";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+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) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ const expectedData = [
+ {
+ $: { id: "1", test: "hello" },
+ subitem:
+ [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ {
+ $: { id: "2" },
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }];
+ const actualData: string[] = [];
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', actualData)
+ // console.log('dataEventCount=', dataEventCount)
+ should(actualData).deepEqual(expectedData);
+ should(dataEventCount).equal(2);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly generate objects with passed attrs and text keys in the options.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item", attrsKey: "attrs", textKey: "text" });
+ const expectedData = [
+ {
+ attrs: { id: "1", test: "hello" },
+ subitem:
+ [{ attrs: { sub: "TESTING SUB" }, text: "one" },
+ { attrs: { sub: "2" }, text: "two" }]
+ },
+ {
+ attrs: { id: "2" },
+ subitem: [{ text: "three" }, { text: "four" }, { text: "five" }]
+ }];
+ const actualData: string[] = [];
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', JSON.stringify(actualData, null, 1))
+ // console.log('dataEventCount=', dataEventCount)
+ should(actualData).deepEqual(expectedData);
+ should(dataEventCount).equal(2);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ 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 parser = new XmlParser({ resourcePath: "/items/item", attrsKey: "!", textKey: "%" });
+ const expectedData = [
+ {
+ "!": { id: "1", test: "hello" },
+ "subitem":
+ [{ "!": { sub: "TESTING SUB" }, "%": "one" },
+ { "!": { sub: "2" }, "%": "two" }]
+ },
+ {
+ "!": { id: "2" },
+ "subitem": [{ "%": "three" }, { "%": "four" }, { "%": "five" }]
+ }];
+ const actualData: string[] = [];
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', JSON.stringify(actualData, null, 1))
+ // console.log('dataEventCount=', dataEventCount)
+ should(actualData).deepEqual(expectedData);
+ should(dataEventCount).equal(2);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/test/parse.spec.ts b/test/parse.spec.ts
new file mode 100644
index 0000000..5753a55
--- /dev/null
+++ b/test/parse.spec.ts
@@ -0,0 +1,97 @@
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+describe("Parse function should work properly", () => {
+ it("should properly parse a simple file.", (done) => {
+ const xml = fs.readFileSync("./test/TestFiles/item.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ const expectedData = [
+ {
+ $: { id: "1", test: "hello" },
+ subitem:
+ [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ {
+ $: { id: "2" },
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }];
+
+ parser.parse(xml.toString(), (err, data) => {
+ if (err) { done(err); }
+ should(data).deepEqual(expectedData);
+ done();
+ });
+ });
+
+ it("should properly parse a medium size file.", (done) => {
+ const xml = fs.readFileSync("./test/TestFiles/medium.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+
+ parser.parse(xml, (err, data) => {
+ if (err) { done(err); }
+ should(data.length).equal(10);
+ done();
+ });
+ });
+
+ it("should properly parse a file containing many nodes.", (done) => {
+ const xml = fs.readFileSync("./test/TestFiles/manyItems.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+
+ parser.parse(xml, (err, data) => {
+ if (err) { done(err); }
+ should(data.length).equal(296);
+ 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 parser = new XmlParser({ resourcePath: "/items/item" });
+ const expectedData = [{
+ $: { id: "1", test: "hello" }, _: " item one two",
+ subitem: [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ {
+ $: { id: "2" }, _: " item one two three four",
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }
+ ];
+
+ parser.parse(xml, (err, data) => {
+ if (err) { done(err); }
+ should(data).deepEqual(expectedData);
+ should(data.length).equal(2);
+ done();
+ });
+ });
+
+ it("should properly parse a huge file.", (done) => {
+ const xml = fs.readFileSync("./test/TestFiles/hugeFile.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ // console.log(parser)
+ parser.parse(xml, (err, data) => {
+ if (err) { done(err); }
+ should(data.length).equal(2072);
+ done();
+ });
+ });
+
+ it("should properly return error if the xml file is corrupted.", (done) => {
+ const xml = fs.readFileSync("./test/TestFiles/corrupted.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+
+ parser.parse(xml, (err, data) => {
+ // console.log(err)
+ should(err.message).equal("mismatched tag at line no: 11");
+ should(data).not.be.ok();
+ done();
+ });
+ });
+});
diff --git a/test/pause_and_resume.spec.ts b/test/pause_and_resume.spec.ts
new file mode 100644
index 0000000..1934acb
--- /dev/null
+++ b/test/pause_and_resume.spec.ts
@@ -0,0 +1,82 @@
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+describe("pause and resume", () => {
+ it("should properly parse a simple file.", function(done) {
+ const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ const expectedData = [
+ {
+ $: { id: "1", test: "hello" },
+ subitem:
+ [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ {
+ $: { id: "2" },
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }];
+ const actualData: string[] = [];
+ let dataEventCount = 0;
+ let isSetTimeoutHappened = true;
+ this.timeout(4000);
+ parser.on("data", (data) => {
+ actualData.push(data);
+ parser.pause();
+ should(isSetTimeoutHappened).equal(true);
+ setTimeout(() => {
+ parser.resume();
+ isSetTimeoutHappened = true;
+ }, 1000);
+ dataEventCount++;
+ isSetTimeoutHappened = false;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', actualData)
+ // console.log('dataEventCount=', dataEventCount)
+ should(actualData).deepEqual(expectedData);
+ should(dataEventCount).equal(2);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ 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 parser = new XmlParser({ resourcePath: "/items/item" });
+
+ let dataEventCount = 0;
+ let isSetTimeoutHappened = true;
+ this.timeout(20000);
+ parser.on("data", (data) => {
+ parser.pause();
+ should(isSetTimeoutHappened).equal(true);
+ setTimeout(() => {
+ parser.resume();
+ isSetTimeoutHappened = true;
+ }, 1000);
+ dataEventCount++;
+ isSetTimeoutHappened = false;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(10);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/test/performance.spec.ts b/test/performance.spec.ts
new file mode 100644
index 0000000..fe714be
--- /dev/null
+++ b/test/performance.spec.ts
@@ -0,0 +1,103 @@
+
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+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 rsStream = fs.createReadStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml')
+ let dataEventCount = 0;
+ // var maxRSSMemoryTaken = 0
+ // var rss
+ const startTime = Date.now();
+ const xmlStream = new stream.Readable();
+ xmlStream._read = function noop() {
+ // nop
+ };
+ let dataChunk;
+ this.timeout(900000);
+ const firstChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/firstChunk.xml");
+ xmlStream.push(firstChunk);
+ for (let i = 0; i < 2200; i++) {
+ dataChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/repetitiveChunk.xml");
+ xmlStream.push(dataChunk);
+ }
+
+ const endingChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/endingChunk.xml");
+ xmlStream.push(endingChunk);
+ xmlStream.push(null);
+ parser.on("data", (data) => {
+ // rss = process.memoryUsage().rss
+ // if (rss > maxRSSMemoryTaken) maxRSSMemoryTaken = rss
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ should(err).not.be.ok();
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ // console.log('RSS memory=', rss)
+ const TimeTaken = Date.now() - startTime;
+ // console.log('time taken=', TimeTaken)
+ should(TimeTaken).be.belowOrEqual(300000);
+ should(dataEventCount).equal(4558400);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse more than 1 GB 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 rsStream = fs.createReadStream('./test/TestFiles/MB_and_GB_size_files/MBFile.xml')
+ let dataEventCount = 0;
+ // var maxRSSMemoryTaken = 0
+ // var rss
+ const startTime = Date.now();
+ const xmlStream = new stream.Readable();
+ xmlStream._read = function noop() {
+ // nop
+ };
+ let dataChunk;
+ this.timeout(900000);
+ const firstChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/firstChunk.xml");
+ xmlStream.push(firstChunk);
+ for (let i = 0; i < 4400; i++) {
+ dataChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/repetitiveChunk.xml");
+ xmlStream.push(dataChunk);
+ }
+
+ const endingChunk = fs.readFileSync("./test/TestFiles/MB_and_GB_size_files/endingChunk.xml");
+ xmlStream.push(endingChunk);
+ xmlStream.push(null);
+ parser.on("data", (data) => {
+ // rss = process.memoryUsage().rss
+ // if (rss > maxRSSMemoryTaken) maxRSSMemoryTaken = rss
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ should(err).not.be.ok();
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ // console.log('RSS memory=', rss)
+ const TimeTaken = Date.now() - startTime;
+ // console.log('time taken=', TimeTaken)
+ should(TimeTaken).be.belowOrEqual(700000);
+ should(dataEventCount).equal(9116800);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/test/read.spec.ts b/test/read.spec.ts
new file mode 100644
index 0000000..3deeea8
--- /dev/null
+++ b/test/read.spec.ts
@@ -0,0 +1,105 @@
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+
+describe("read method", () => {
+ it("should properly parse a simple file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ const expectedData = [
+ {
+ $: { id: "1", test: "hello" },
+ subitem:
+ [{ $: { sub: "TESTING SUB" }, _: "one" },
+ { $: { sub: "2" }, _: "two" }]
+ },
+ {
+ $: { id: "2" },
+ subitem: [{ _: "three" }, { _: "four" }, { _: "five" }]
+ }];
+ const actualData: string[] = [];
+ let obj;
+ let Timeout: any;
+
+ parser.on("readable", () => {
+ Timeout = setInterval(() => {
+ obj = parser.read();
+ if (obj) {
+ actualData.push(obj);
+ }
+ obj = null;
+ }, 50);
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', actualData)
+ // console.log('dataEventCount=', dataEventCount)
+ clearInterval(Timeout);
+ should(actualData).deepEqual(expectedData);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a file containing many nodes.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ let objCount = 0;
+ const endEventOcurred = false;
+
+ parser.on("readable", () => {
+ read();
+ });
+
+ function read() {
+ while (parser.read()) { objCount++; }
+ if (!endEventOcurred) { setTimeout(read, 50); }
+ }
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log(objCount)
+ should(objCount).deepEqual(296);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a huge.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ let objCount = 0;
+ const endEventOcurred = false;
+
+ parser.on("readable", () => {
+ read();
+ });
+
+ function read() {
+ while (parser.read()) { objCount++; }
+ if (!endEventOcurred) { setTimeout(read, 50); }
+ }
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log(objCount)
+ should(objCount).deepEqual(2072);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/test/same_name.spec.ts b/test/same_name.spec.ts
new file mode 100644
index 0000000..cbde5ed
--- /dev/null
+++ b/test/same_name.spec.ts
@@ -0,0 +1,111 @@
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+
+describe("nodes with same names", () => {
+ it("should properly parse a simple file containing nodes with same names.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/nodesWithSameNames.xml");
+ const parser = new XmlParser();
+
+ const actualData: string[] = [];
+ const actualItems: string[] = [];
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ should(err).not.be.ok();
+ done(err);
+ });
+
+ parser.on("item", (item) => {
+ actualItems.push(item);
+ });
+
+ parser.on("end", () => {
+ should(actualItems.length).equal(18);
+ should(dataEventCount).equal(18);
+ done();
+ });
+
+ xmlStream.pipe(parser);
+ });
+
+ 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 parser = new XmlParser();
+
+ let dataEventCount = 0;
+ let itemEventCount = 0;
+ let subitemEventCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ should(err).not.be.ok();
+ done(err);
+ });
+
+ parser.on("item", (item) => {
+ itemEventCount++;
+ });
+
+ parser.on("subitem", (subitem) => {
+ subitemEventCount++;
+ });
+
+ parser.on("end", () => {
+ should(itemEventCount).equal(18);
+ should(subitemEventCount).equal(13);
+ should(dataEventCount).equal(31);
+ done();
+ });
+
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a medium size file with same names randomly.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/nodesWithSameNamesRandomly.xml");
+ const parser = new XmlParser();
+
+ let dataEventCount = 0;
+ let itemEventCount = 0;
+ let subitemEventCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("item", (item) => {
+ itemEventCount++;
+ });
+
+ parser.on("subitem", (subitem) => {
+ subitemEventCount++;
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ // console.log('itemEventCount=', itemEventCount)
+ // console.log('subitemEventCount=', subitemEventCount)
+ should(dataEventCount).equal(32);
+ should(itemEventCount).equal(19);
+ should(subitemEventCount).equal(13);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/test/test.js b/test/test.js
deleted file mode 100644
index 18528c2..0000000
--- a/test/test.js
+++ /dev/null
@@ -1,1545 +0,0 @@
-var should = require('should')
-var fs = require('fs')
-var zlib = require('zlib')
-var stream = require('stream')
-
-var ParserFactory = require('../parser')
-
-describe('Tests', function () {
- describe('Basic behaviour', function () {
- it('should properly parse a simple file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var expectedData = [
- { '$': { id: '1', test: 'hello' },
- subitem:
- [ { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' } ] },
- { '$': { id: '2' },
- subitem: [ { _: 'three' }, { _: 'four' }, { _: 'five' } ] } ]
- var actualData = []
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- should(err).not.be.ok()
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', actualData)
- // console.log('dataEventCount=', dataEventCount)
- actualData.should.deepEqual(expectedData)
- dataEventCount.should.equal(2)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a medium size file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
-
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(10)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a file containing many nodes.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
-
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(296)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a xml simple file in which nodes contain text values randomly.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/randomText.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var expectedData = [ { '$': { 'id': '1', 'test': 'hello' }, '_': ' item one two',
- 'subitem': [ { '$': { 'sub': 'TESTING SUB' }, '_': 'one' },
- { '$': { 'sub': '2' }, '_': 'two' } ] },
- { '$': { 'id': '2' }, '_': ' item one two three four',
- 'subitem': [ { '_': 'three' }, { '_': 'four' }, { '_': 'five' } ] }
- ]
- var actualData = []
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', JSON.stringify(actualData, null, 1))
- // console.log('dataEventCount=', dataEventCount)
- actualData.should.deepEqual(expectedData)
- dataEventCount.should.equal(2)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a huge file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/hugeFile.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- // console.log(parser)
- var dataEventCount = 0
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(2072)
- done()
- })
- xmlStream.pipe(parser)
- })
- })
-
- describe('pause and resume', function () {
- it('should properly parse a simple file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var expectedData = [
- { '$': { id: '1', test: 'hello' },
- subitem:
- [ { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' } ] },
- { '$': { id: '2' },
- subitem: [ { _: 'three' }, { _: 'four' }, { _: 'five' } ] } ]
- var actualData = []
- var dataEventCount = 0
- var isSetTimeoutHappened = true
- this.timeout(4000)
- parser.on('data', function (data) {
- actualData.push(data)
- parser.pause()
- isSetTimeoutHappened.should.equal(true)
- setTimeout(function () {
- parser.resume()
- isSetTimeoutHappened = true
- }, 3000)
- dataEventCount++
- isSetTimeoutHappened = false
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', actualData)
- // console.log('dataEventCount=', dataEventCount)
- actualData.should.deepEqual(expectedData)
- dataEventCount.should.equal(2)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should emit data events with 1sec interval between each using pause and resume.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
-
- var dataEventCount = 0
- var isSetTimeoutHappened = true
- this.timeout(20000)
- parser.on('data', function (data) {
- parser.pause()
- isSetTimeoutHappened.should.equal(true)
- setTimeout(function () {
- parser.resume()
- isSetTimeoutHappened = true
- }, 2000)
- dataEventCount++
- isSetTimeoutHappened = false
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(10)
- done()
- })
- xmlStream.pipe(parser)
- })
- })
-
- describe('should respect the options passed', function () {
- it('should properly generate objects with $ as key for attrs and _ as key for text value of node.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var expectedData = [
- { '$': { id: '1', test: 'hello' },
- subitem:
- [ { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' } ] },
- { '$': { id: '2' },
- subitem: [ { _: 'three' }, { _: 'four' }, { _: 'five' } ] } ]
- var actualData = []
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', actualData)
- // console.log('dataEventCount=', dataEventCount)
- actualData.should.deepEqual(expectedData)
- dataEventCount.should.equal(2)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly generate objects with passed attrs and text keys in the options.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', attrsKey: 'attrs', textKey: 'text'})
- var expectedData = [
- { 'attrs': { id: '1', test: 'hello' },
- subitem:
- [ { 'attrs': { sub: 'TESTING SUB' }, text: 'one' },
- { 'attrs': { sub: '2' }, text: 'two' } ] },
- { 'attrs': { id: '2' },
- subitem: [ { text: 'three' }, { text: 'four' }, { text: 'five' } ] } ]
- var actualData = []
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', JSON.stringify(actualData, null, 1))
- // console.log('dataEventCount=', dataEventCount)
- actualData.should.deepEqual(expectedData)
- dataEventCount.should.equal(2)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly generate objects when special symbols are passed as attrs and text keys in the options.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', attrsKey: '!', textKey: '%'})
- var expectedData = [
- { '!': { id: '1', test: 'hello' },
- subitem:
- [ { '!': { sub: 'TESTING SUB' }, '%': 'one' },
- { '!': { sub: '2' }, '%': 'two' } ] },
- { '!': { id: '2' },
- subitem: [ { '%': 'three' }, { '%': 'four' }, { '%': 'five' } ] } ]
- var actualData = []
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', JSON.stringify(actualData, null, 1))
- // console.log('dataEventCount=', dataEventCount)
- actualData.should.deepEqual(expectedData)
- dataEventCount.should.equal(2)
- done()
- })
- xmlStream.pipe(parser)
- })
- })
-
- describe('should properly handle uncompressed files', function () {
- it('should properly parse a uncompressed xml file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var gzip = zlib.createGzip()
- var gunzip = zlib.createGunzip()
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(10)
- done()
- })
- xmlStream.pipe(gzip).pipe(gunzip).pipe(parser)
- })
-
- it('should properly parse uncompressed file and go fine with pause and resume.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var gzip = zlib.createGzip()
- var gunzip = zlib.createGunzip()
-
- var dataEventCount = 0
- var isSetTimeoutHappened = true
- this.timeout(20000)
- parser.on('data', function (data) {
- parser.pause()
- isSetTimeoutHappened.should.equal(true)
- setTimeout(function () {
- parser.resume()
- isSetTimeoutHappened = true
- }, 2000)
- dataEventCount++
- isSetTimeoutHappened = false
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(10)
- done()
- })
- xmlStream.pipe(gzip).pipe(gunzip).pipe(parser)
- })
- })
-
- describe('read method', function () {
- it('should properly parse a simple file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var expectedData = [
- { '$': { id: '1', test: 'hello' },
- subitem:
- [ { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' } ] },
- { '$': { id: '2' },
- subitem: [ { _: 'three' }, { _: 'four' }, { _: 'five' } ] } ]
- var actualData = []
- var obj
- var Timeout
-
- parser.on('readable', function () {
- Timeout = setInterval(function () {
- if ((obj = parser.read())) actualData.push(obj)
- obj = null
- }, 50)
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', actualData)
- // console.log('dataEventCount=', dataEventCount)
- clearInterval(Timeout)
- actualData.should.deepEqual(expectedData)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a file containing many nodes.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var objCount = 0
- var endEventOcurred = false
-
- parser.on('readable', function () {
- read()
- })
-
- function read () {
- while (parser.read()) objCount++
- if (!endEventOcurred) setTimeout(read, 50)
- }
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log(objCount)
- objCount.should.deepEqual(296)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a huge.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/hugeFile.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var objCount = 0
- var endEventOcurred = false
-
- parser.on('readable', function () {
- read()
- })
-
- function read () {
- while (parser.read()) objCount++
- if (!endEventOcurred) setTimeout(read, 50)
- }
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log(objCount)
- objCount.should.deepEqual(2072)
- done()
- })
- xmlStream.pipe(parser)
- })
- })
-
- describe('Error Handling', function () {
- it('should properly return error if the xml file is corrupted.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/corrupted.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- // console.log(err)
- err.message.should.equal('mismatched tag at line no: 11')
- done()
- })
-
- xmlStream.pipe(parser)
- })
-
- it('should properly return error if the large xml file is corrupted.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/largeCorruptedFile.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- // console.log(err)
- err.message.should.equal('mismatched tag at line no: 8346')
- done()
- })
-
- xmlStream.pipe(parser)
- })
- })
-
- describe('CData and comments in xml', function () {
- it('should properly parse a simple file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/CData-comments.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
-
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(296)
- done()
- })
- xmlStream.pipe(parser)
- })
- })
-
- describe('emitOnNodeName', function () {
- it('should properly emit events on node names.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', emitOnNodeName: true})
- var expectedData = [
- { '$': { id: '1', test: 'hello' },
- subitem:
- [ { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' } ] },
- { '$': { id: '2' },
- subitem: [ { _: 'three' }, { _: 'four' }, { _: 'five' } ] } ]
- var actualData = []
- var itemData = []
- var dataEventCount = 0
- var itemCount = 0
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('item', function (item) {
- itemData.push(item)
- itemCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', actualData)
- // console.log('dataEventCount=', dataEventCount)
- actualData.should.deepEqual(expectedData)
- dataEventCount.should.equal(2)
- itemData.should.deepEqual(expectedData)
- itemCount.should.equal(2)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly emit events on node names while parsing a medium size file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', emitOnNodeName: true})
-
- var dataEventCount = 0
- var itemCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('item', function (data) {
- itemCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(10)
- itemCount.should.equal(10)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a file containing many nodes.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', emitOnNodeName: true})
-
- var dataEventCount = 0
- var itemCount = 0
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('item', function (data) {
- itemCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(296)
- itemCount.should.equal(296)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a huge file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/hugeFile.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', emitOnNodeName: true})
-
- var dataEventCount = 0
- var itemCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('item', function (item) {
- itemCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(2072)
- itemCount.should.equal(2072)
- done()
- })
- xmlStream.pipe(parser)
- })
- })
-
- describe('wrong resourcePath', function () {
- it('should be able to detect the wrong resourcePath at root level.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
- var parser = new ParserFactory({resourcePath: '/wrong/noNodes', emitOnNodeName: true})
-
- var actualData = []
- var itemData = []
- var dataEventCount = 0
- var itemCount = 0
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('item', function (item) {
- itemData.push(item)
- itemCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', actualData)
- // console.log('dataEventCount=', dataEventCount)
- actualData.length.should.equal(0)
- dataEventCount.should.equal(0)
- itemData.length.should.equal(0)
- itemCount.should.equal(0)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should be able to detect wrong resourcePath while parsing xml', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
- var parser = new ParserFactory({resourcePath: '/wrong/noNodes', emitOnNodeName: true})
-
- var dataEventCount = 0
- var itemCount = 0
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('item', function (data) {
- itemCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(0)
- itemCount.should.equal(0)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a huge file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/hugeFile.xml')
- var parser = new ParserFactory({resourcePath: '/wrong/path', emitOnNodeName: true})
-
- var dataEventCount = 0
- var itemCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('item', function (item) {
- itemCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- dataEventCount.should.equal(0)
- itemCount.should.equal(0)
- done()
- })
- xmlStream.pipe(parser)
- })
- })
-
- describe('interested Nodes', function () {
- it('should properly parse a simple file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
- var parser = new ParserFactory()
-
- var expectedData =
- [
- { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' },
- { '$': { id: '1', test: 'hello' },
- subitem: [ { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' } ]
- },
- { _: 'three' },
- { _: 'four' },
- { _: 'five' },
- { '$': { id: '2' },
- subitem: [ { _: 'three' }, { _: 'four' }, { _: 'five' } ] }
- ]
-
- var actualData = []
- var dataEventCount = 0
- var expectedItems = [
- { '$': { id: '1', test: 'hello' },
- subitem:
- [ { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' } ] },
- { '$': { id: '2' },
- subitem: [ { _: 'three' }, { _: 'four' }, { _: 'five' } ] } ]
- var actualItems = []
- var actualSubitems = []
- var expectedSubitems = [
- { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' },
- { _: 'three' },
- { _: 'four' },
- { _: 'five' }
- ]
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- should(err).not.be.ok()
- done(err)
- })
-
- parser.on('item', function (item) {
- actualItems.push(item)
- })
-
- parser.on('subitem', function (subitem) {
- actualSubitems.push(subitem)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', JSON.stringify(actualData, null, 1))
- // console.log('dataEventCount=', dataEventCount)
- actualData.should.deepEqual(expectedData)
- actualItems.should.deepEqual(expectedItems)
- actualSubitems.should.deepEqual(expectedSubitems)
- actualSubitems.length.should.equal(5)
- actualItems.length.should.equal(2)
- dataEventCount.should.equal(7)
- done()
- })
-
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a medium size file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/medium.xml')
- var parser = new ParserFactory()
-
- var dataEventCount = 0
- var itemEventCount = 0
- var subitemEventCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('item', function (item) {
- itemEventCount++
- })
-
- parser.on('subitem', function (subitem) {
- subitemEventCount++
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- // console.log('itemEventCount=', itemEventCount)
- // console.log('subitemEventCount=', subitemEventCount)
- dataEventCount.should.equal(31)
- itemEventCount.should.equal(10)
- subitemEventCount.should.equal(21)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a file containing many nodes.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/manyItems.xml')
- var parser = new ParserFactory()
-
- var dataEventCount = 0
- var itemEventCount = 0
- var subitemEventCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('item', function (item) {
- itemEventCount++
- })
-
- parser.on('subitem', function (subitem) {
- subitemEventCount++
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- // console.log('itemEventCount=', itemEventCount)
- // console.log('subitemEventCount=', subitemEventCount)
- itemEventCount.should.equal(296)
- subitemEventCount.should.equal(600)
- dataEventCount.should.equal(896)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a xml simple file in which nodes contain text values randomly.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/randomText.xml')
- var parser = new ParserFactory()
- var expectedData =
- [
- { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' },
- { '$': { id: '1', test: 'hello' }, _: ' item one two',
- subitem: [ { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' } ]
- },
- { _: 'three' },
- { _: 'four' },
- { _: 'five' },
- { '$': { id: '2' }, '_': ' item one two three four',
- subitem: [ { _: 'three' }, { _: 'four' }, { _: 'five' } ] }
- ]
- var expectedItems = [
- { '$': { id: '1', test: 'hello' }, _: ' item one two',
- subitem:
- [ { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' } ] },
- { '$': { id: '2' }, '_': ' item one two three four',
- subitem: [ { _: 'three' }, { _: 'four' }, { _: 'five' } ] } ]
- var actualItems = []
- var actualSubitems = []
- var expectedSubitems = [
- { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' },
- { _: 'three' },
- { _: 'four' },
- { _: 'five' }
- ]
- var actualData = []
- var dataEventCount = 0
- var itemEventCount = 0
- var subitemEventCount = 0
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('item', function (item) {
- itemEventCount++
- actualItems.push(item)
- })
-
- parser.on('subitem', function (subitem) {
- subitemEventCount++
- actualSubitems.push(subitem)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', JSON.stringify(actualData, null, 1))
- // console.log('dataEventCount=', dataEventCount)
- // console.log('itemEventCount=', itemEventCount)
- // console.log('subitemEventCount=', subitemEventCount)
- actualData.should.deepEqual(expectedData)
- actualItems.should.deepEqual(expectedItems)
- actualSubitems.should.deepEqual(expectedSubitems)
- dataEventCount.should.equal(7)
- itemEventCount.should.equal(2)
- subitemEventCount.should.equal(5)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a huge file.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/hugeFile.xml')
- var parser = new ParserFactory()
-
- var dataEventCount = 0
- var itemEventCount = 0
- var subitemEventCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('item', function (item) {
- itemEventCount++
- })
-
- parser.on('subitem', function (subitem) {
- subitemEventCount++
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- // console.log('itemEventCount=', itemEventCount)
- // console.log('subitemEventCount=', subitemEventCount)
- dataEventCount.should.equal(6272)
- itemEventCount.should.equal(2072)
- subitemEventCount.should.equal(4200)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a simple file and return when root element when listening on it.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
- var parser = new ParserFactory()
- var expectedData =
- [{ 'item': [{ '$': { 'id': '1', 'test': 'hello' },
- 'subitem': [{ '$': { 'sub': 'TESTING SUB' }, '_': 'one' },
- { '$': { 'sub': '2' }, '_': 'two' }]
- },
- { '$': { 'id': '2' }, 'subitem': [{ '_': 'three' }, { '_': 'four' },
- { '_': 'five' }]
- }]
- }]
-
- var actualData = []
- var dataEventCount = 0
- var itemsEventCount = 0
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- should(err).not.be.ok()
- done(err)
- })
-
- parser.on('items', function (item) {
- itemsEventCount++
- })
-
- parser.on('end', function () {
- // console.log('actualData=', JSON.stringify(actualData, null, 1))
- // console.log('dataEventCount=', dataEventCount)
- // console.log('itemEventCount=', itemsEventCount)
- actualData.should.deepEqual(expectedData)
- itemsEventCount.should.equal(1)
- dataEventCount.should.equal(1)
- done()
- })
- xmlStream.pipe(parser)
- })
- })
-
- describe.skip('performance testing', function () {
- it('should properly parse more than 500 MB of file.', function (done) {
- var parser = new ParserFactory({resourcePath: '/items/item'})
- // 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 dataEventCount = 0
- // var maxRSSMemoryTaken = 0
- // var rss
- var startTime = Date.now()
- var xmlStream = new stream.Readable()
- xmlStream._read = function noop () {}
- var dataChunk
- this.timeout(900000)
- var firstChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/firstChunk.xml')
- xmlStream.push(firstChunk)
- for (var i = 0; i < 2200; i++) {
- dataChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/repetitiveChunk.xml')
- xmlStream.push(dataChunk)
- }
-
- var endingChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/endingChunk.xml')
- xmlStream.push(endingChunk)
- xmlStream.push(null)
- parser.on('data', function (data) {
- // rss = process.memoryUsage().rss
- // if (rss > maxRSSMemoryTaken) maxRSSMemoryTaken = rss
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- should(err).not.be.ok()
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- // console.log('RSS memory=', rss)
- var TimeTaken = Date.now() - startTime
- // console.log('time taken=', TimeTaken)
- TimeTaken.should.be.belowOrEqual(300000)
- dataEventCount.should.equal(4558400)
- done()
- })
- xmlStream.pipe(parser)
- })
-
- it('should properly parse more than 1 GB of file.', function (done) {
- var parser = new ParserFactory({resourcePath: '/items/item'})
- // 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 dataEventCount = 0
- // var maxRSSMemoryTaken = 0
- // var rss
- var startTime = Date.now()
- var xmlStream = new stream.Readable()
- xmlStream._read = function noop () {}
- var dataChunk
- this.timeout(900000)
- var firstChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/firstChunk.xml')
- xmlStream.push(firstChunk)
- for (var i = 0; i < 4400; i++) {
- dataChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/repetitiveChunk.xml')
- xmlStream.push(dataChunk)
- }
-
- var endingChunk = fs.readFileSync('./test/TestFiles/MB_and_GB_size_files/endingChunk.xml')
- xmlStream.push(endingChunk)
- xmlStream.push(null)
- parser.on('data', function (data) {
- // rss = process.memoryUsage().rss
- // if (rss > maxRSSMemoryTaken) maxRSSMemoryTaken = rss
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- should(err).not.be.ok()
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- // console.log('RSS memory=', rss)
- var TimeTaken = Date.now() - startTime
- // console.log('time taken=', TimeTaken)
- TimeTaken.should.be.belowOrEqual(700000)
- dataEventCount.should.equal(9116800)
- done()
- })
- xmlStream.pipe(parser)
- })
- })
-
- describe('nodes with same names', function () {
- it('should properly parse a simple file containing nodes with same names.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/nodesWithSameNames.xml')
- var parser = new ParserFactory()
-
- var actualData = []
- var actualItems = []
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- should(err).not.be.ok()
- done(err)
- })
-
- parser.on('item', function (item) {
- actualItems.push(item)
- })
-
- parser.on('end', function () {
- actualItems.length.should.equal(18)
- dataEventCount.should.equal(18)
- done()
- })
-
- xmlStream.pipe(parser)
- })
-
- it('should properly parse a simple file containing nodes with same names and emit events on multiple nodes.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/nodesWithSameNames.xml')
- var parser = new ParserFactory()
-
- var dataEventCount = 0
- var itemEventCount = 0
- var subitemEventCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- should(err).not.be.ok()
- done(err)
- })
-
- parser.on('item', function (item) {
- itemEventCount++
- })
-
- parser.on('subitem', function (subitem) {
- subitemEventCount++
- })
-
- parser.on('end', function () {
- itemEventCount.should.equal(18)
- subitemEventCount.should.equal(13)
- dataEventCount.should.equal(31)
- done()
- })
-
- xmlStream.pipe(parser)
- })
-
- it.skip('should properly parse a medium size file with same names randomly.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/nodesWithSameNamesRandomly.xml')
- var parser = new ParserFactory()
-
- var dataEventCount = 0
- var itemEventCount = 0
- var subitemEventCount = 0
-
- parser.on('data', function (data) {
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('item', function (item) {
- itemEventCount++
- })
-
- parser.on('subitem', function (subitem) {
- subitemEventCount++
- })
-
- parser.on('end', function () {
- // console.log('dataEventCount=', dataEventCount)
- // console.log('itemEventCount=', itemEventCount)
- // console.log('subitemEventCount=', subitemEventCount)
- dataEventCount.should.equal(32)
- itemEventCount.should.equal(19)
- subitemEventCount.should.equal(13)
- done()
- })
- xmlStream.pipe(parser)
- })
- })
-
- describe('Parse funtion should work properly', function () {
- it('should properly parse a simple file.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/item.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var expectedData = [
- { '$': { id: '1', test: 'hello' },
- subitem:
- [ { '$': { sub: 'TESTING SUB' }, _: 'one' },
- { '$': { sub: '2' }, _: 'two' } ] },
- { '$': { id: '2' },
- subitem: [ { _: 'three' }, { _: 'four' }, { _: 'five' } ] } ]
-
- parser.parse(xml.toString(), function (err, data) {
- if (err) done(err)
- data.should.deepEqual(expectedData)
- done()
- })
- })
-
- it('should properly parse a medium size file.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/medium.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
-
- parser.parse(xml, function (err, data) {
- if (err) done(err)
- data.length.should.equal(10)
- done()
- })
- })
-
- it('should properly parse a file containing many nodes.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/manyItems.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
-
- parser.parse(xml, function (err, data) {
- if (err) done(err)
- data.length.should.equal(296)
- done()
- })
- })
-
- it('should properly parse a xml simple file in which nodes contain text values randomly.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/randomText.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- var expectedData = [ { '$': { 'id': '1', 'test': 'hello' }, '_': ' item one two',
- 'subitem': [ { '$': { 'sub': 'TESTING SUB' }, '_': 'one' },
- { '$': { 'sub': '2' }, '_': 'two' } ] },
- { '$': { 'id': '2' }, '_': ' item one two three four',
- 'subitem': [ { '_': 'three' }, { '_': 'four' }, { '_': 'five' } ] }
- ]
-
- parser.parse(xml, function (err, data) {
- if (err) done(err)
- data.should.deepEqual(expectedData)
- data.length.should.equal(2)
- done()
- })
- })
-
- it('should properly parse a huge file.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/hugeFile.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
- // console.log(parser)
- parser.parse(xml, function (err, data) {
- if (err) done(err)
- data.length.should.equal(2072)
- done()
- })
- })
-
- it('should properly return error if the xml file is corrupted.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/corrupted.xml')
- var parser = new ParserFactory({resourcePath: '/items/item'})
-
- parser.parse(xml, function (err, data) {
- // console.log(err)
- err.message.should.equal('mismatched tag at line no: 11')
- should(data).not.be.ok()
- done()
- })
- })
- })
-
- describe('should respect explicitArray constructor option', function () {
- it('should properly parse a simple file with explicitArray set to false.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/item.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', explicitArray: false})
- var expectedData = [
- { '$': { id: '1', test: 'hello' },
- subitem: { '$': { sub: '2' }, _: 'two' } },
- { '$': { id: '2' },
- subitem: { _: 'five' } } ]
-
- parser.parse(xml.toString(), function (err, data) {
- if (err) done(err)
- // console.log('data=', JSON.stringify(data))
- data.should.deepEqual(expectedData)
- done()
- })
- })
-
- it('should properly parse a medium size file with explicitArray set to false.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/medium.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', explicitArray: false})
- var expectedData = [
- {
- "$":{
- "id":"1",
- "test":"hello"
- },
- "subitem":{
- "$":{
- "sub":"2"
- },
- "_":"two"
- }
- },
- {
- "$":{
- "id":"2"
- },
- "subitem":{
- "_":"five"
- }
- },
- {
- "$":{
- "id":"3",
- "test":"hello"
- },
- "subitem":{
- "$":{
- "sub":"2"
- },
- "_":"two"
- }
- },
- {
- "$":{
- "id":"4",
- "test":"hello"
- },
- "subitem":{
- "$":{
- "sub":"2"
- },
- "_":"two"
- }
- },
- {
- "$":{
- "id":"5",
- "test":"hello"
- },
- "subitem":{
- "$":{
- "sub":"2"
- },
- "_":"two"
- }
- },
- {
- "$":{
- "id":"6",
- "test":"hello"
- },
- "subitem":{
- "$":{
- "sub":"2"
- },
- "_":"two"
- }
- },
- {
- "$":{
- "id":"7",
- "test":"hello"
- },
- "subitem":{
- "$":{
- "sub":"2"
- },
- "_":"two"
- }
- },
- {
- "$":{
- "id":"8",
- "test":"hello"
- },
- "subitem":{
- "$":{
- "sub":"2"
- },
- "_":"two"
- }
- },
- {
- "$":{
- "id":"9",
- "test":"hello"
- },
- "subitem":{
- "$":{
- "sub":"2"
- },
- "_":"two"
- }
- },
- {
- "$":{
- "id":"10",
- "test":"hello"
- },
- "subitem":{
- "$":{
- "sub":"2"
- },
- "_":"two"
- }
- }
- ]
- parser.parse(xml, function (err, data) {
- if (err) done(err)
-
- data.should.deepEqual(expectedData)
- data.length.should.equal(10)
- done()
- })
- })
-
- it('should properly parse a file containing many nodes when explicitArray set to false.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/manyItems.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', explicitArray: false})
-
- parser.parse(xml, function (err, data) {
- if (err) done(err)
-
- data.length.should.equal(296)
- done()
- })
- })
-
- it('should properly parse a xml simple file in which nodes contain text values randomly when explicitArray set to false.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/randomText.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', explicitArray: false})
- var expectedData = [ { '$': { 'id': '1', 'test': 'hello' }, '_': ' item one two',
- 'subitem': { '$': { 'sub': '2' }, '_': 'two' } },
- { '$': { 'id': '2' }, '_': ' item one two three four',
- 'subitem': { '_': 'five' } }
- ]
-
- parser.parse(xml, function (err, data) {
- if (err) done(err)
-
- data.should.deepEqual(expectedData)
- data.length.should.equal(2)
- done()
- })
- })
-
- it('should properly parse a huge file with explicitArray set to false.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/hugeFile.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', explicitArray: false})
- // console.log(parser)
- parser.parse(xml, function (err, data) {
- if (err) done(err)
- data.length.should.equal(2072)
- done()
- })
- })
-
- it('should properly return error if the xml file is corrupted.', function (done) {
- var xml = fs.readFileSync('./test/TestFiles/corrupted.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', explicitArray: false})
-
- parser.parse(xml, function (err, data) {
- // console.log(err)
- err.message.should.equal('mismatched tag at line no: 11')
- should(data).not.be.ok()
- done()
- })
- })
-
- it('should properly generate objects when special symbols are passed as attrs and text keys and explicitArray is false in the options.', function (done) {
- var xmlStream = fs.createReadStream('./test/TestFiles/item.xml')
- var parser = new ParserFactory({resourcePath: '/items/item', attrsKey: '!', textKey: '%', explicitArray: false})
- var expectedData = [
- { '!': { id: '1', test: 'hello' },
- subitem: { '!': { sub: '2' }, '%': 'two' } },
- { '!': { id: '2' },
- subitem: { '%': 'five' } } ]
- var actualData = []
- var dataEventCount = 0
-
- parser.on('data', function (data) {
- actualData.push(data)
- dataEventCount++
- })
-
- parser.on('error', function (err) {
- done(err)
- })
-
- parser.on('end', function () {
- // console.log('actualData=', JSON.stringify(actualData, null, 1))
- // console.log('dataEventCount=', dataEventCount)
- actualData.should.deepEqual(expectedData)
- dataEventCount.should.equal(2)
- done()
- })
- xmlStream.pipe(parser)
- })
- })
-})
diff --git a/test/uncompressed.spec.ts b/test/uncompressed.spec.ts
new file mode 100644
index 0000000..b823826
--- /dev/null
+++ b/test/uncompressed.spec.ts
@@ -0,0 +1,64 @@
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+
+describe("should properly handle uncompressed files", () => {
+ it("should properly parse a uncompressed xml file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ const gzip = zlib.createGzip();
+ const gunzip = zlib.createGunzip();
+ let dataEventCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(10);
+ done();
+ });
+ xmlStream.pipe(gzip).pipe(gunzip).pipe(parser);
+ });
+
+ it("should properly parse uncompressed file and go fine with pause and resume.", function(done) {
+ const xmlStream = fs.createReadStream("./test/TestFiles/medium.xml");
+ const parser = new XmlParser({ resourcePath: "/items/item" });
+ const gzip = zlib.createGzip();
+ const gunzip = zlib.createGunzip();
+
+ let dataEventCount = 0;
+ let isSetTimeoutHappened = true;
+ this.timeout(20000);
+ parser.on("data", (data) => {
+ parser.pause();
+ should(isSetTimeoutHappened).equal(true);
+ setTimeout(() => {
+ parser.resume();
+ isSetTimeoutHappened = true;
+ }, 2000);
+ dataEventCount++;
+ isSetTimeoutHappened = false;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(10);
+ done();
+ });
+ xmlStream.pipe(gzip).pipe(gunzip).pipe(parser);
+ });
+});
diff --git a/test/wrong_resourcepath.spec.ts b/test/wrong_resourcepath.spec.ts
new file mode 100644
index 0000000..ee548b3
--- /dev/null
+++ b/test/wrong_resourcepath.spec.ts
@@ -0,0 +1,99 @@
+import fs from "fs";
+import "mocha";
+import should from "should";
+import stream from "stream";
+import zlib from "zlib";
+
+import { XmlParser } from "../src/parser";
+
+describe("wrong resourcePath", () => {
+ it("should be able to detect the wrong resourcePath at root level.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/item.xml");
+ const parser = new XmlParser({ resourcePath: "/wrong/noNodes", emitOnNodeName: true });
+
+ const actualData : string[] = [];
+ const itemData : string[] = [];
+ let dataEventCount = 0;
+ let itemCount = 0;
+
+ parser.on("data", (data) => {
+ actualData.push(data);
+ dataEventCount++;
+ });
+
+ parser.on("item", (item) => {
+ itemData.push(item);
+ itemCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('actualData=', actualData)
+ // console.log('dataEventCount=', dataEventCount)
+ should(actualData.length).equal(0);
+ should(dataEventCount).equal(0);
+ should(itemData.length).equal(0);
+ should(itemCount).equal(0);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should be able to detect wrong resourcePath while parsing xml", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/manyItems.xml");
+ const parser = new XmlParser({ resourcePath: "/wrong/noNodes", emitOnNodeName: true });
+
+ let dataEventCount = 0;
+ let itemCount = 0;
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("item", (data) => {
+ itemCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(0);
+ should(itemCount).equal(0);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+
+ it("should properly parse a huge file.", (done) => {
+ const xmlStream = fs.createReadStream("./test/TestFiles/hugeFile.xml");
+ const parser = new XmlParser({ resourcePath: "/wrong/path", emitOnNodeName: true });
+
+ let dataEventCount = 0;
+ let itemCount = 0;
+
+ parser.on("data", (data) => {
+ dataEventCount++;
+ });
+
+ parser.on("item", (item) => {
+ itemCount++;
+ });
+
+ parser.on("error", (err) => {
+ done(err);
+ });
+
+ parser.on("end", () => {
+ // console.log('dataEventCount=', dataEventCount)
+ should(dataEventCount).equal(0);
+ should(itemCount).equal(0);
+ done();
+ });
+ xmlStream.pipe(parser);
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..f783190
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,33 @@
+{
+ "compilerOptions": {
+ "types": [
+ "node"
+ ],
+ "outDir": "dist",
+ "moduleResolution": "node",
+ "module": "commonjs",
+ "removeComments": true,
+ "sourceMap": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports":true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "noImplicitAny": true,
+ "declaration": true,
+ "resolveJsonModule": true,
+ "target":"es2017",
+ "lib": ["es2017"],
+ "typeRoots": [
+ "./node_modules/@types",
+ "./typings"
+ ]
+ },
+ "include": [
+ "./typings/**/*.d.ts",
+ "src/**/*"
+ ],
+ "exclude": [
+ "node_modules",
+ "dist"
+ ]
+}
\ No newline at end of file
diff --git a/tslint.json b/tslint.json
new file mode 100644
index 0000000..cca3b93
--- /dev/null
+++ b/tslint.json
@@ -0,0 +1,22 @@
+{
+ "rules": {
+ "no-console": [false],
+ "variable-name": false,
+ "radix": false,
+ "object-literal-sort-keys": false,
+ "trailing-comma":[false],
+ "indent": [true,"tabs"],
+ "max-line-length": [false],
+ "no-string-literal": false,
+ "class-name": false,
+ "no-namespace": [false],
+ "no-bitwise": false
+ },
+ "extends": "tslint:recommended",
+ "linterOptions": {
+ "exclude":[
+ "*.json",
+ "**/*.json"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/typings/node-expat/index.d.ts b/typings/node-expat/index.d.ts
new file mode 100644
index 0000000..79f88d3
--- /dev/null
+++ b/typings/node-expat/index.d.ts
@@ -0,0 +1,91 @@
+// Type definitions for node-expat 2.3.x
+// Project: http://nodejs.org/
+// Definitions by: Microsoft TypeScript
+// Oleksandr Fedorchuk
+// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
+
+
+declare module "node-expat" {
+ import { Stream } from "stream";
+ import { EventEmitter } from "events";
+
+ interface NameSpace {
+ [key: string]: ValueType;
+ }
+
+ interface TypedEmitter> extends EventEmitter {
+ addListener (event: Event, listener: (...args: EventMapType[Event]) => void): this;
+ on (event: Event, listener: (...args: EventMapType[Event]) => void): this;
+ once (event: Event, listener: (...args: EventMapType[Event]) => void): this;
+ prependListener (event: Event, listener: (...args: EventMapType[Event]) => void): this;
+ prependOnceListener (event: Event, listener: (...args: EventMapType[Event]) => void): this;
+ removeListener (event: Event, listener: (...args: EventMapType[Event]) => void): this;
+ emit (event: Event, ...args: EventMapType[Event]): boolean;
+
+ addListener (event: string | symbol, listener: (...args: any[]) => void): this;
+ on (event: string | symbol, listener: (...args: any[]) => void): this;
+
+
+ once (event: string | symbol, listener: (...args: any[]) => void): this;
+ prependListener (event: string | symbol, listener: (...args: any[]) => void): this;
+ prependOnceListener (event: string | symbol, listener: (...args: any[]) => void): this;
+ removeListener (event: string | symbol, listener: (...args: any[]) => void): this;
+ emit (event: string | symbol, ...args: any[]): boolean;
+ }
+
+ interface ParserEventsMap extends NameSpace {
+ startElement: [string, NameSpace];
+ endElement: [string];
+ text: [string];
+ comment: [string];
+ processingInstruction: [string, string];
+ xmlDecl: [string | null, string | null, boolean];
+ startCdata: [];
+ endCdata: [];
+ entityDecl: [string | null, boolean, string | null, string | null, string | null, string | null, string | null];
+ end: [];
+ close: [];
+ error: [string | Error];
+ }
+
+ export class Parser extends Stream implements NodeJS.WritableStream, TypedEmitter
+ {
+ readonly writable: boolean;
+ stop(): this;
+
+ pause(): this;
+ resume(): this;
+
+ parse(buf: string | Buffer, isFinal?: boolean): boolean;
+ setEncoding(value: string): this;
+ /// @TODO setUnknownEncoding
+ getError(): string | null;
+ destroy(): this;
+ destroySoon(): this;
+
+ write(buffer: Buffer | string, cb?: Function): boolean;
+ write(str: string, encoding?: string, cb?: Function): boolean;
+ end(cb?: Function): boolean;
+ end(chunk: Buffer, cb?: Function): boolean;
+ end(chunk: string, cb?: Function): boolean;
+ end(chunk: string, encding?: string, cb?: Function): boolean;
+ reset(this: Parser): this;
+ getCurrentLineNumber(this: Parser): number;
+ getCurrentColumnNumber(this: Parser): number;
+ getCurrentByteIndex(this: Parser): number;
+
+ on (event: "startElement", listener:(name:string, attrs: any)=>void):this;
+ on (event: "endElement", listener:(name:string)=>void):this;
+ on (event: "text", listener:(text:string)=>void):this;
+ on (event: "processingInstruction", listener:(target:any, data:any)=>void):this;
+ on (event: "comment", listener:(s:string)=>void):this;
+ on (event: "xmlDecl", listener:(version:string, encoding: string, standalone: boolean)=>void):this;
+ on (event: "startCdata", listener:()=>void):this;
+ on (event: "endCdata", listener:()=>void):this;
+ on (event: "entityDecl", listener:(entityName:string, isParameterEntity:boolean, value:string, base:string, systemId:string, publicId:string, notationName:string)=>void):this;
+ on (event: "error", listener:(e:Error)=>void):this;
+ on (event: "close", listener:()=>void):this;
+ }
+
+ export function createParser(callback?: (name: string, attributes: NameSpace) => void): Parser;
+}
diff --git a/typings/node-expat/project.json b/typings/node-expat/project.json
new file mode 100644
index 0000000..a514dfe
--- /dev/null
+++ b/typings/node-expat/project.json
@@ -0,0 +1,3 @@
+{
+
+}
\ No newline at end of file