fast-xml-parser 5.3.7 → 5.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/lib/fxbuilder.min.js +1 -1
- package/lib/fxbuilder.min.js.map +1 -1
- package/lib/fxp.cjs +1 -1
- package/lib/fxp.d.cts +7 -0
- package/lib/fxp.min.js +1 -1
- package/lib/fxp.min.js.map +1 -1
- package/lib/fxparser.min.js +1 -1
- package/lib/fxparser.min.js.map +1 -1
- package/lib/fxvalidator.min.js +1 -1
- package/lib/fxvalidator.min.js.map +1 -1
- package/package.json +1 -1
- package/src/fxp.d.ts +9 -0
- package/src/v6/EntitiesParser.js +87 -87
- package/src/v6/OptionsBuilder.js +10 -10
- package/src/v6/OutputBuilders/BaseOutputBuilder.js +23 -23
- package/src/v6/OutputBuilders/JsArrBuilder.js +29 -29
- package/src/v6/OutputBuilders/JsObjBuilder.js +39 -39
- package/src/v6/OutputBuilders/ParserOptionsBuilder.js +17 -17
- package/src/v6/XMLParser.js +22 -22
- package/src/v6/valueParsers/EntitiesParser.js +85 -85
- package/src/validator.js +34 -34
- package/src/xmlbuilder/json2xml.js +49 -49
- package/src/xmlbuilder/orderedJs2Xml.js +14 -3
- package/src/xmlparser/DocTypeReader.js +1 -1
- package/src/xmlparser/OptionsBuilder.js +1 -0
- package/src/xmlparser/OrderedObjParser.js +3 -0
- package/src/xmlparser/XMLParser.js +21 -21
- package/src/xmlparser/node2json.js +38 -34
- package/src/xmlparser/xmlNode.js +10 -10
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {buildOptions,registerCommonValueParsers} from './ParserOptionsBuilder.js';
|
|
1
|
+
import { buildOptions, registerCommonValueParsers } from './ParserOptionsBuilder.js';
|
|
2
2
|
|
|
3
|
-
export default class OutputBuilder{
|
|
4
|
-
constructor(options){
|
|
3
|
+
export default class OutputBuilder {
|
|
4
|
+
constructor(options) {
|
|
5
5
|
this.options = buildOptions(options);
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
this.registeredParsers = registerCommonValueParsers(this.options);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
registerValueParser(name, parserInstance) {//existing name will override the parser without warning
|
|
10
|
+
this.registeredParsers[name] = parserInstance;
|
|
11
|
+
}
|
|
12
12
|
|
|
13
|
-
getInstance(parserOptions){
|
|
13
|
+
getInstance(parserOptions) {
|
|
14
14
|
return new JsArrBuilder(parserOptions, this.options, this.registeredParsers);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -18,9 +18,9 @@ export default class OutputBuilder{
|
|
|
18
18
|
const rootName = '!js_arr';
|
|
19
19
|
import BaseOutputBuilder from './BaseOutputBuilder.js';
|
|
20
20
|
|
|
21
|
-
class JsArrBuilder extends BaseOutputBuilder{
|
|
21
|
+
class JsArrBuilder extends BaseOutputBuilder {
|
|
22
22
|
|
|
23
|
-
constructor(parserOptions, options,registeredParsers) {
|
|
23
|
+
constructor(parserOptions, options, registeredParsers) {
|
|
24
24
|
super();
|
|
25
25
|
this.tagsStack = [];
|
|
26
26
|
this.parserOptions = parserOptions;
|
|
@@ -32,10 +32,10 @@ class JsArrBuilder extends BaseOutputBuilder{
|
|
|
32
32
|
this.attributes = {};
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
addTag(tag){
|
|
35
|
+
addTag(tag) {
|
|
36
36
|
//when a new tag is added, it should be added as child of current node
|
|
37
37
|
//TODO: shift this check to the parser
|
|
38
|
-
if(tag.name === "__proto__") tag.name = "#__proto__";
|
|
38
|
+
if (tag.name === "__proto__") tag.name = "#__proto__";
|
|
39
39
|
|
|
40
40
|
this.tagsStack.push(this.currentNode);
|
|
41
41
|
this.currentNode = new Node(tag.name, this.attributes);
|
|
@@ -47,23 +47,23 @@ class JsArrBuilder extends BaseOutputBuilder{
|
|
|
47
47
|
* @param {Node} node
|
|
48
48
|
* @returns boolean: true if the node should not be added
|
|
49
49
|
*/
|
|
50
|
-
closeTag(){
|
|
50
|
+
closeTag() {
|
|
51
51
|
const node = this.currentNode;
|
|
52
52
|
this.currentNode = this.tagsStack.pop(); //set parent node in scope
|
|
53
|
-
if(this.options.onClose !== undefined){
|
|
53
|
+
if (this.options.onClose !== undefined) {
|
|
54
54
|
//TODO TagPathMatcher
|
|
55
|
-
const resultTag = this.options.onClose(node,
|
|
56
|
-
new TagPathMatcher(this.tagsStack,node));
|
|
55
|
+
const resultTag = this.options.onClose(node,
|
|
56
|
+
new TagPathMatcher(this.tagsStack, node));
|
|
57
57
|
|
|
58
|
-
if(resultTag) return;
|
|
58
|
+
if (resultTag) return;
|
|
59
59
|
}
|
|
60
60
|
this.currentNode.child.push(node); //to parent node
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
//Called by parent class methods
|
|
64
|
-
_addChild(key, val){
|
|
64
|
+
_addChild(key, val) {
|
|
65
65
|
// if(key === "__proto__") tagName = "#__proto__";
|
|
66
|
-
this.currentNode.child.push(
|
|
66
|
+
this.currentNode.child.push({ [key]: val });
|
|
67
67
|
// this.currentNode.leafType = false;
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -71,31 +71,31 @@ class JsArrBuilder extends BaseOutputBuilder{
|
|
|
71
71
|
* Add text value child node
|
|
72
72
|
* @param {string} text
|
|
73
73
|
*/
|
|
74
|
-
addValue(text){
|
|
75
|
-
this.currentNode.child.push(
|
|
74
|
+
addValue(text) {
|
|
75
|
+
this.currentNode.child.push({ [this.options.nameFor.text]: this.parseValue(text, this.options.tags.valueParsers) });
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
addPi(name){
|
|
78
|
+
addPi(name) {
|
|
79
79
|
//TODO: set pi flag
|
|
80
|
-
if(!this.options.ignorePiTags){
|
|
80
|
+
if (!this.options.ignorePiTags) {
|
|
81
81
|
const node = new Node(name, this.attributes);
|
|
82
82
|
this.currentNode[":@"] = this.attributes;
|
|
83
83
|
this.currentNode.child.push(node);
|
|
84
84
|
}
|
|
85
85
|
this.attributes = {};
|
|
86
86
|
}
|
|
87
|
-
getOutput(){
|
|
87
|
+
getOutput() {
|
|
88
88
|
return this.root.child[0];
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
class Node{
|
|
95
|
-
constructor(tagname, attributes){
|
|
94
|
+
class Node {
|
|
95
|
+
constructor(tagname, attributes) {
|
|
96
96
|
this.tagname = tagname;
|
|
97
97
|
this.child = []; //nested tags, text, cdata, comments
|
|
98
|
-
if(attributes && Object.keys(attributes).length > 0)
|
|
98
|
+
if (attributes && Object.keys(attributes).length > 0)
|
|
99
99
|
this[":@"] = attributes;
|
|
100
100
|
}
|
|
101
101
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
import {buildOptions,registerCommonValueParsers} from './ParserOptionsBuilder.js';
|
|
3
|
+
import { buildOptions, registerCommonValueParsers } from './ParserOptionsBuilder.js';
|
|
4
4
|
|
|
5
|
-
export default class OutputBuilder{
|
|
6
|
-
constructor(builderOptions){
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
export default class OutputBuilder {
|
|
6
|
+
constructor(builderOptions) {
|
|
7
|
+
this.options = buildOptions(builderOptions);
|
|
8
|
+
this.registeredParsers = registerCommonValueParsers(this.options);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
registerValueParser(name,parserInstance){//existing name will override the parser without warning
|
|
11
|
+
registerValueParser(name, parserInstance) {//existing name will override the parser without warning
|
|
12
12
|
this.registeredParsers[name] = parserInstance;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
getInstance(parserOptions){
|
|
15
|
+
getInstance(parserOptions) {
|
|
16
16
|
return new JsObjBuilder(parserOptions, this.options, this.registeredParsers);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -20,9 +20,9 @@ export default class OutputBuilder{
|
|
|
20
20
|
import BaseOutputBuilder from './BaseOutputBuilder.js';
|
|
21
21
|
const rootName = '^';
|
|
22
22
|
|
|
23
|
-
class JsObjBuilder extends BaseOutputBuilder{
|
|
23
|
+
class JsObjBuilder extends BaseOutputBuilder {
|
|
24
24
|
|
|
25
|
-
constructor(parserOptions, builderOptions,registeredParsers) {
|
|
25
|
+
constructor(parserOptions, builderOptions, registeredParsers) {
|
|
26
26
|
super();
|
|
27
27
|
//hold the raw detail of a tag and sequence with reference to the output
|
|
28
28
|
this.tagsStack = [];
|
|
@@ -38,14 +38,14 @@ class JsObjBuilder extends BaseOutputBuilder{
|
|
|
38
38
|
this.attributes = {};
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
addTag(tag){
|
|
41
|
+
addTag(tag) {
|
|
42
42
|
|
|
43
43
|
let value = "";
|
|
44
|
-
if(
|
|
44
|
+
if (!isEmpty(this.attributes)) {
|
|
45
45
|
value = {};
|
|
46
|
-
if(this.options.attributes.groupBy){
|
|
46
|
+
if (this.options.attributes.groupBy) {
|
|
47
47
|
value[this.options.attributes.groupBy] = this.attributes;
|
|
48
|
-
}else{
|
|
48
|
+
} else {
|
|
49
49
|
value = this.attributes;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -62,44 +62,44 @@ class JsObjBuilder extends BaseOutputBuilder{
|
|
|
62
62
|
* @param {Node} node
|
|
63
63
|
* @returns boolean: true if the node should not be added
|
|
64
64
|
*/
|
|
65
|
-
closeTag(){
|
|
65
|
+
closeTag() {
|
|
66
66
|
const tagName = this.tagName;
|
|
67
67
|
let value = this.value;
|
|
68
68
|
let textValue = this.textValue;
|
|
69
69
|
|
|
70
70
|
//update tag text value
|
|
71
|
-
if(typeof value !== "object" && !Array.isArray(value)){
|
|
71
|
+
if (typeof value !== "object" && !Array.isArray(value)) {
|
|
72
72
|
value = this.parseValue(textValue.trim(), this.options.tags.valueParsers);
|
|
73
|
-
}else if(textValue.length > 0){
|
|
73
|
+
} else if (textValue.length > 0) {
|
|
74
74
|
value[this.options.nameFor.text] = this.parseValue(textValue.trim(), this.options.tags.valueParsers);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
let resultTag= {
|
|
77
|
+
|
|
78
|
+
let resultTag = {
|
|
79
79
|
tagName: tagName,
|
|
80
80
|
value: value
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
-
if(this.options.onTagClose !== undefined){
|
|
83
|
+
if (this.options.onTagClose !== undefined) {
|
|
84
84
|
//TODO TagPathMatcher
|
|
85
|
-
resultTag = this.options.onClose(tagName, value, this.textValue, new TagPathMatcher(this.tagsStack,node));
|
|
85
|
+
resultTag = this.options.onClose(tagName, value, this.textValue, new TagPathMatcher(this.tagsStack, node));
|
|
86
86
|
|
|
87
|
-
if(!resultTag) return;
|
|
87
|
+
if (!resultTag) return;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
//set parent node in scope
|
|
91
|
-
let arr = this.tagsStack.pop();
|
|
91
|
+
let arr = this.tagsStack.pop();
|
|
92
92
|
let parentTag = arr[2];
|
|
93
|
-
parentTag=this._addChildTo(resultTag.tagName, resultTag.value, parentTag);
|
|
93
|
+
parentTag = this._addChildTo(resultTag.tagName, resultTag.value, parentTag);
|
|
94
94
|
|
|
95
95
|
this.tagName = arr[0];
|
|
96
96
|
this.textValue = arr[1];
|
|
97
97
|
this.value = parentTag;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
_addChild(key, val){
|
|
101
|
-
if(typeof this.value === "string"){
|
|
102
|
-
this.value = { [this.options.nameFor.text]
|
|
100
|
+
_addChild(key, val) {
|
|
101
|
+
if (typeof this.value === "string") {
|
|
102
|
+
this.value = { [this.options.nameFor.text]: this.value };
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
this._addChildTo(key, val, this.value);
|
|
@@ -107,12 +107,12 @@ class JsObjBuilder extends BaseOutputBuilder{
|
|
|
107
107
|
this.attributes = {};
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
_addChildTo(key, val, node){
|
|
111
|
-
if(typeof node === 'string') node = {};
|
|
112
|
-
if(!node[key]){
|
|
110
|
+
_addChildTo(key, val, node) {
|
|
111
|
+
if (typeof node === 'string') node = {};
|
|
112
|
+
if (!node[key]) {
|
|
113
113
|
node[key] = val;
|
|
114
|
-
}else{ //Repeated
|
|
115
|
-
if(!Array.isArray(node[key])){ //but not stored as array
|
|
114
|
+
} else { //Repeated
|
|
115
|
+
if (!Array.isArray(node[key])) { //but not stored as array
|
|
116
116
|
node[key] = [node[key]];
|
|
117
117
|
}
|
|
118
118
|
node[key].push(val);
|
|
@@ -125,26 +125,26 @@ class JsObjBuilder extends BaseOutputBuilder{
|
|
|
125
125
|
* Add text value child node
|
|
126
126
|
* @param {string} text
|
|
127
127
|
*/
|
|
128
|
-
addValue(text){
|
|
128
|
+
addValue(text) {
|
|
129
129
|
//TODO: use bytes join
|
|
130
|
-
if(this.textValue.length > 0) this.textValue += " " + text;
|
|
130
|
+
if (this.textValue.length > 0) this.textValue += " " + text;
|
|
131
131
|
else this.textValue = text;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
addPi(name){
|
|
134
|
+
addPi(name) {
|
|
135
135
|
let value = "";
|
|
136
|
-
if(
|
|
136
|
+
if (!isEmpty(this.attributes)) {
|
|
137
137
|
value = {};
|
|
138
|
-
if(this.options.attributes.groupBy){
|
|
138
|
+
if (this.options.attributes.groupBy) {
|
|
139
139
|
value[this.options.attributes.groupBy] = this.attributes;
|
|
140
|
-
}else{
|
|
140
|
+
} else {
|
|
141
141
|
value = this.attributes;
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
this._addChild(name, value);
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
}
|
|
147
|
-
getOutput(){
|
|
147
|
+
getOutput() {
|
|
148
148
|
return this.value;
|
|
149
149
|
}
|
|
150
150
|
}
|
|
@@ -3,8 +3,8 @@ import booleanParser from "../valueParsers/booleanParser.js";
|
|
|
3
3
|
import currencyParser from "../valueParsers/currency.js";
|
|
4
4
|
import numberParser from "../valueParsers/number.js";
|
|
5
5
|
|
|
6
|
-
const defaultOptions={
|
|
7
|
-
nameFor:{
|
|
6
|
+
const defaultOptions = {
|
|
7
|
+
nameFor: {
|
|
8
8
|
text: "#text",
|
|
9
9
|
comment: "",
|
|
10
10
|
cdata: "",
|
|
@@ -22,11 +22,11 @@ const defaultOptions={
|
|
|
22
22
|
// "date",
|
|
23
23
|
]
|
|
24
24
|
},
|
|
25
|
-
attributes:{
|
|
25
|
+
attributes: {
|
|
26
26
|
prefix: "@_",
|
|
27
27
|
suffix: "",
|
|
28
28
|
groupBy: "",
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
valueParsers: [
|
|
31
31
|
// "trim",
|
|
32
32
|
// "boolean",
|
|
@@ -35,35 +35,35 @@ const defaultOptions={
|
|
|
35
35
|
// "date",
|
|
36
36
|
]
|
|
37
37
|
},
|
|
38
|
-
dataType:{
|
|
38
|
+
dataType: {
|
|
39
39
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
//TODO
|
|
44
|
-
const withJoin = ["trim","join", /*"entities",*/"number","boolean","currency"/*, "date"*/]
|
|
45
|
-
const withoutJoin = ["trim", /*"entities",*/"number","boolean","currency"/*, "date"*/]
|
|
44
|
+
const withJoin = ["trim", "join", /*"entities",*/"number", "boolean", "currency"/*, "date"*/]
|
|
45
|
+
const withoutJoin = ["trim", /*"entities",*/"number", "boolean", "currency"/*, "date"*/]
|
|
46
46
|
|
|
47
|
-
export function buildOptions(options){
|
|
47
|
+
export function buildOptions(options) {
|
|
48
48
|
//clone
|
|
49
|
-
const finalOptions = { ...
|
|
49
|
+
const finalOptions = { ...defaultOptions };
|
|
50
50
|
|
|
51
51
|
//add config missed in cloning
|
|
52
52
|
finalOptions.tags.valueParsers.push(...withJoin)
|
|
53
|
-
if(!this.preserveOrder)
|
|
53
|
+
if (!this.preserveOrder)
|
|
54
54
|
finalOptions.tags.valueParsers.push(...withoutJoin);
|
|
55
55
|
|
|
56
56
|
//add config missed in cloning
|
|
57
57
|
finalOptions.attributes.valueParsers.push(...withJoin)
|
|
58
58
|
|
|
59
59
|
//override configuration
|
|
60
|
-
copyProperties(finalOptions,options);
|
|
60
|
+
copyProperties(finalOptions, options);
|
|
61
61
|
return finalOptions;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
function copyProperties(target, source) {
|
|
65
65
|
for (let key in source) {
|
|
66
|
-
if (
|
|
66
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
67
67
|
if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
68
68
|
// Recursively copy nested properties
|
|
69
69
|
if (typeof target[key] === 'undefined') {
|
|
@@ -78,16 +78,16 @@ function copyProperties(target, source) {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
export function registerCommonValueParsers(options){
|
|
81
|
+
export function registerCommonValueParsers(options) {
|
|
82
82
|
return {
|
|
83
83
|
"trim": new trimParser(),
|
|
84
84
|
// "join": this.entityParser.parse,
|
|
85
85
|
"boolean": new booleanParser(),
|
|
86
86
|
"number": new numberParser({
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
hex: true,
|
|
88
|
+
leadingZeros: true,
|
|
89
|
+
eNotation: true
|
|
90
|
+
}),
|
|
91
91
|
"currency": new currencyParser(),
|
|
92
92
|
// "date": this.entityParser.parse,
|
|
93
93
|
}
|
package/src/v6/XMLParser.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { buildOptions} from './OptionsBuilder.js';
|
|
1
|
+
import { buildOptions } from './OptionsBuilder.js';
|
|
2
2
|
import Xml2JsParser from './Xml2JsParser.js';
|
|
3
3
|
|
|
4
|
-
export default class XMLParser{
|
|
5
|
-
|
|
6
|
-
constructor(options){
|
|
4
|
+
export default class XMLParser {
|
|
5
|
+
|
|
6
|
+
constructor(options) {
|
|
7
7
|
this.externalEntities = {};
|
|
8
8
|
this.options = buildOptions(options);
|
|
9
9
|
// console.log(this.options)
|
|
@@ -13,17 +13,17 @@ export default class XMLParser{
|
|
|
13
13
|
* @param {string|Buffer} xmlData
|
|
14
14
|
* @param {boolean|Object} validationOption
|
|
15
15
|
*/
|
|
16
|
-
parse(xmlData){
|
|
17
|
-
if(Array.isArray(xmlData) && xmlData.byteLength !== undefined){
|
|
16
|
+
parse(xmlData) {
|
|
17
|
+
if (Array.isArray(xmlData) && xmlData.byteLength !== undefined) {
|
|
18
18
|
return this.parse(xmlData);
|
|
19
|
-
}else if(
|
|
19
|
+
} else if (xmlData.toString) {
|
|
20
20
|
xmlData = xmlData.toString();
|
|
21
|
-
}else{
|
|
21
|
+
} else {
|
|
22
22
|
throw new Error("XML data is accepted in String or Bytes[] form.")
|
|
23
23
|
}
|
|
24
24
|
// if( validationOption){
|
|
25
25
|
// if(validationOption === true) validationOption = {}; //validate with default options
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
// const result = validator.validate(xmlData, validationOption);
|
|
28
28
|
// if (result !== true) {
|
|
29
29
|
// throw Error( `${result.err.msg}:${result.err.line}:${result.err.col}` )
|
|
@@ -38,9 +38,9 @@ export default class XMLParser{
|
|
|
38
38
|
* @param {string|Buffer} xmlData
|
|
39
39
|
* @param {boolean|Object} validationOption
|
|
40
40
|
*/
|
|
41
|
-
parseBytesArr(xmlData){
|
|
42
|
-
if(Array.isArray(xmlData) && xmlData.byteLength !== undefined){
|
|
43
|
-
}else{
|
|
41
|
+
parseBytesArr(xmlData) {
|
|
42
|
+
if (Array.isArray(xmlData) && xmlData.byteLength !== undefined) {
|
|
43
|
+
} else {
|
|
44
44
|
throw new Error("XML data is accepted in Bytes[] form.")
|
|
45
45
|
}
|
|
46
46
|
const parser = new Xml2JsParser(this.options);
|
|
@@ -51,9 +51,9 @@ export default class XMLParser{
|
|
|
51
51
|
* Parse XML data stream to JS object
|
|
52
52
|
* @param {fs.ReadableStream} xmlDataStream
|
|
53
53
|
*/
|
|
54
|
-
parseStream(xmlDataStream){
|
|
55
|
-
if(!isStream(xmlDataStream)) throw new Error("FXP: Invalid stream input");
|
|
56
|
-
|
|
54
|
+
parseStream(xmlDataStream) {
|
|
55
|
+
if (!isStream(xmlDataStream)) throw new Error("FXP: Invalid stream input");
|
|
56
|
+
|
|
57
57
|
const orderedObjParser = new Xml2JsParser(this.options);
|
|
58
58
|
orderedObjParser.entityParser.addExternalEntities(this.externalEntities);
|
|
59
59
|
return orderedObjParser.parseStream(xmlDataStream);
|
|
@@ -64,20 +64,20 @@ export default class XMLParser{
|
|
|
64
64
|
* @param {string} key
|
|
65
65
|
* @param {string} value
|
|
66
66
|
*/
|
|
67
|
-
addEntity(key, value){
|
|
68
|
-
if(value.indexOf("&") !== -1){
|
|
67
|
+
addEntity(key, value) {
|
|
68
|
+
if (value.indexOf("&") !== -1) {
|
|
69
69
|
throw new Error("Entity value can't have '&'")
|
|
70
|
-
}else if(key.indexOf("&") !== -1 || key.indexOf(";") !== -1){
|
|
70
|
+
} else if (key.indexOf("&") !== -1 || key.indexOf(";") !== -1) {
|
|
71
71
|
throw new Error("An entity must be set without '&' and ';'. Eg. use '#xD' for '
'")
|
|
72
|
-
}else if(value === "&"){
|
|
72
|
+
} else if (value === "&") {
|
|
73
73
|
throw new Error("An entity with value '&' is not permitted");
|
|
74
|
-
}else{
|
|
74
|
+
} else {
|
|
75
75
|
this.externalEntities[key] = value;
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function isStream(stream){
|
|
81
|
-
if(stream && typeof stream.read === "function" && typeof stream.on === "function" && typeof stream.readableEnded === "boolean") return true;
|
|
80
|
+
function isStream(stream) {
|
|
81
|
+
if (stream && typeof stream.read === "function" && typeof stream.on === "function" && typeof stream.readableEnded === "boolean") return true;
|
|
82
82
|
return false;
|
|
83
83
|
}
|
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
const ampEntity = { regex: /&(amp|#38|#x26);/g, val
|
|
1
|
+
const ampEntity = { regex: /&(amp|#38|#x26);/g, val: "&" };
|
|
2
2
|
const htmlEntities = {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
3
|
+
"space": { regex: /&(nbsp|#160);/g, val: " " },
|
|
4
|
+
// "lt" : { regex: /&(lt|#60);/g, val: "<" },
|
|
5
|
+
// "gt" : { regex: /&(gt|#62);/g, val: ">" },
|
|
6
|
+
// "amp" : { regex: /&(amp|#38);/g, val: "&" },
|
|
7
|
+
// "quot" : { regex: /&(quot|#34);/g, val: "\"" },
|
|
8
|
+
// "apos" : { regex: /&(apos|#39);/g, val: "'" },
|
|
9
|
+
"cent": { regex: /&(cent|#162);/g, val: "¢" },
|
|
10
|
+
"pound": { regex: /&(pound|#163);/g, val: "£" },
|
|
11
|
+
"yen": { regex: /&(yen|#165);/g, val: "¥" },
|
|
12
|
+
"euro": { regex: /&(euro|#8364);/g, val: "€" },
|
|
13
|
+
"copyright": { regex: /&(copy|#169);/g, val: "©" },
|
|
14
|
+
"reg": { regex: /&(reg|#174);/g, val: "®" },
|
|
15
|
+
"inr": { regex: /&(inr|#8377);/g, val: "₹" },
|
|
16
|
+
"num_dec": { regex: /&#([0-9]{1,7});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 10)) },
|
|
17
|
+
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val: (_, str) => String.fromCodePoint(Number.parseInt(str, 16)) },
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
export default class EntitiesParser{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
export default class EntitiesParser {
|
|
21
|
+
constructor(replaceHtmlEntities) {
|
|
22
|
+
this.replaceHtmlEntities = replaceHtmlEntities;
|
|
23
|
+
this.docTypeEntities = {};
|
|
24
|
+
this.lastEntities = {
|
|
25
|
+
"apos": { regex: /&(apos|#39|#x27);/g, val: "'" },
|
|
26
|
+
"gt": { regex: /&(gt|#62|#x3E);/g, val: ">" },
|
|
27
|
+
"lt": { regex: /&(lt|#60|#x3C);/g, val: "<" },
|
|
28
|
+
"quot": { regex: /&(quot|#34|#x22);/g, val: "\"" },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
32
|
+
addExternalEntities(externalEntities) {
|
|
33
|
+
const entKeys = Object.keys(externalEntities);
|
|
34
|
+
for (let i = 0; i < entKeys.length; i++) {
|
|
35
|
+
const ent = entKeys[i];
|
|
36
|
+
this.addExternalEntity(ent, externalEntities[ent])
|
|
38
37
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
38
|
+
}
|
|
39
|
+
addExternalEntity(key, val) {
|
|
40
|
+
validateEntityName(key);
|
|
41
|
+
if (val.indexOf("&") !== -1) {
|
|
42
|
+
reportWarning(`Entity ${key} is not added as '&' is found in value;`)
|
|
43
|
+
return;
|
|
44
|
+
} else {
|
|
45
|
+
this.lastEntities[ent] = {
|
|
46
|
+
regex: new RegExp("&" + key + ";", "g"),
|
|
47
|
+
val: val
|
|
49
48
|
}
|
|
50
49
|
}
|
|
50
|
+
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
52
|
+
addDocTypeEntities(entities) {
|
|
53
|
+
const entKeys = Object.keys(entities);
|
|
54
|
+
for (let i = 0; i < entKeys.length; i++) {
|
|
55
|
+
const ent = entKeys[i];
|
|
56
|
+
this.docTypeEntities[ent] = {
|
|
57
|
+
regex: new RegExp("&" + ent + ";", "g"),
|
|
58
|
+
val: entities[ent]
|
|
59
|
+
}
|
|
61
60
|
}
|
|
61
|
+
}
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
parse(val) {
|
|
64
|
+
return this.replaceEntitiesValue(val)
|
|
65
|
+
}
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
val = val.replace( ampEntity.regex, ampEntity.val);
|
|
67
|
+
/**
|
|
68
|
+
* 1. Replace DOCTYPE entities
|
|
69
|
+
* 2. Replace external entities
|
|
70
|
+
* 3. Replace HTML entities if asked
|
|
71
|
+
* @param {string} val
|
|
72
|
+
*/
|
|
73
|
+
replaceEntitiesValue(val) {
|
|
74
|
+
if (typeof val === "string" && val.length > 0) {
|
|
75
|
+
for (let entityName in this.docTypeEntities) {
|
|
76
|
+
const entity = this.docTypeEntities[entityName];
|
|
77
|
+
val = val.replace(entity.regx, entity.val);
|
|
78
|
+
}
|
|
79
|
+
for (let entityName in this.lastEntities) {
|
|
80
|
+
const entity = this.lastEntities[entityName];
|
|
81
|
+
val = val.replace(entity.regex, entity.val);
|
|
82
|
+
}
|
|
83
|
+
if (this.replaceHtmlEntities) {
|
|
84
|
+
for (let entityName in htmlEntities) {
|
|
85
|
+
const entity = htmlEntities[entityName];
|
|
86
|
+
val = val.replace(entity.regex, entity.val);
|
|
90
87
|
}
|
|
91
|
-
|
|
88
|
+
}
|
|
89
|
+
val = val.replace(ampEntity.regex, ampEntity.val);
|
|
92
90
|
}
|
|
91
|
+
return val;
|
|
92
|
+
}
|
|
93
93
|
};
|
|
94
94
|
|
|
95
95
|
//an entity name should not contains special characters that may be used in regex
|
|
96
96
|
//Eg !?\\\/[]$%{}^&*()<>
|
|
97
97
|
const specialChar = "!?\\\/[]$%{}^&*()<>|+";
|
|
98
98
|
|
|
99
|
-
function validateEntityName(name){
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
function validateEntityName(name) {
|
|
100
|
+
for (let i = 0; i < specialChar.length; i++) {
|
|
101
|
+
const ch = specialChar[i];
|
|
102
|
+
if (name.indexOf(ch) !== -1) throw new Error(`Invalid character ${ch} in entity name`);
|
|
103
|
+
}
|
|
104
|
+
return name;
|
|
105
105
|
}
|