fast-xml-parser 4.0.0-beta.6 → 4.0.1
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 +22 -4
- package/README.md +15 -5
- package/package.json +3 -2
- package/src/fxp_cjs +8 -0
- package/src/util.js +0 -17
- package/src/validator.js +3 -6
- package/src/xmlbuilder/json2xml.js +36 -66
- package/src/xmlbuilder/orderedJs2Xml.js +5 -7
- package/src/xmlparser/OptionsBuilder.js +2 -29
- package/src/xmlparser/OrderedObjParser.js +42 -69
package/CHANGELOG.md
CHANGED
|
@@ -1,19 +1,37 @@
|
|
|
1
1
|
Note: If you find missing information about particular minor version, that version must have been changed without any functional change in this library.
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**4.0.1 / 2022-01-08**
|
|
4
|
+
* fix builder for pi tag
|
|
5
|
+
* fix: support suppressBooleanAttrs by builder
|
|
6
|
+
|
|
7
|
+
**4.0.0 / 2022-01-06**
|
|
8
|
+
* Generating different combined, parser only, builder only, validator only browser bundles
|
|
9
|
+
* Keeping cjs modules as they can be imported in cjs and esm modules both. Otherwise refer `esm` branch.
|
|
10
|
+
|
|
11
|
+
**4.0.0-beta.8 / 2021-12-13**
|
|
12
|
+
* call tagValueProcessor for stop nodes
|
|
13
|
+
|
|
14
|
+
**4.0.0-beta.7 / 2021-12-09**
|
|
15
|
+
* fix Validator bug when an attribute has no value but '=' only
|
|
16
|
+
* XML Builder should suppress unpaired tags by default.
|
|
17
|
+
* documents update for missing features
|
|
18
|
+
* refactoring to use Object.assign
|
|
19
|
+
* refactoring to remove repeated code
|
|
20
|
+
|
|
21
|
+
**4.0.0-beta.6 / 2021-12-05**
|
|
4
22
|
* Support PI Tags processing
|
|
5
23
|
* Support `suppressBooleanAttributes` by XML Builder for attributes with value `true`.
|
|
6
24
|
|
|
7
|
-
**
|
|
25
|
+
**4.0.0-beta.5 / 2021-12-04**
|
|
8
26
|
* fix: when a tag with name "attributes"
|
|
9
27
|
|
|
10
|
-
**
|
|
28
|
+
**4.0.0-beta.4 / 2021-12-02**
|
|
11
29
|
* Support HTML document parsing
|
|
12
30
|
* skip stop nodes parsing when building the XML from JS object
|
|
13
31
|
* Support external entites without DOCTYPE
|
|
14
32
|
* update dev dependency: strnum v1.0.5 to fix long number issue
|
|
15
33
|
|
|
16
|
-
**
|
|
34
|
+
**4.0.0-beta.3 / 2021-11-30**
|
|
17
35
|
* support global stopNodes expression like "*.stop"
|
|
18
36
|
* support self-closing and paired unpaired tags
|
|
19
37
|
* fix: CDATA should not be parsed.
|
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# [fast-xml-parser](https://www.npmjs.com/package/fast-xml-parser)
|
|
2
2
|
[](#backers) [](#sponsors) [](https://snyk.io/test/github/naturalintelligence/fast-xml-parser)
|
|
3
3
|
[![NPM quality][quality-image]][quality-url]
|
|
4
|
-
[](https://travis-ci.org/NaturalIntelligence/fast-xml-parser)
|
|
5
4
|
[](https://coveralls.io/github/NaturalIntelligence/fast-xml-parser?branch=master)
|
|
6
5
|
[<img src="https://img.shields.io/badge/Try-me-blue.svg?colorA=FFA500&colorB=0000FF" alt="Try me"/>](https://naturalintelligence.github.io/fast-xml-parser/)
|
|
7
6
|
[](https://npm.im/fast-xml-parser)
|
|
@@ -90,7 +89,7 @@ const xmlContent = builder.build(jObj);
|
|
|
90
89
|
|
|
91
90
|
In a HTML page
|
|
92
91
|
```html
|
|
93
|
-
<script src="path/to/
|
|
92
|
+
<script src="path/to/fxp.min.js"></script>
|
|
94
93
|
:
|
|
95
94
|
<script>
|
|
96
95
|
const parser = new fxparser.XMLParser();
|
|
@@ -98,6 +97,16 @@ In a HTML page
|
|
|
98
97
|
</script>
|
|
99
98
|
```
|
|
100
99
|
|
|
100
|
+
Check lib folder for different browser bundles
|
|
101
|
+
|
|
102
|
+
| Bundle Name | Size |
|
|
103
|
+
| -- | -- |
|
|
104
|
+
| fxbuilder.min.js | 5.2K |
|
|
105
|
+
| fxparser.js | 50K |
|
|
106
|
+
| fxparser.min.js | 17K |
|
|
107
|
+
| fxp.min.js | 22K |
|
|
108
|
+
| fxvalidator.min.js | 5.7K |
|
|
109
|
+
|
|
101
110
|
### Documents
|
|
102
111
|
**v3**
|
|
103
112
|
* [documents](./docs/v3/docs.md)
|
|
@@ -107,9 +116,9 @@ In a HTML page
|
|
|
107
116
|
2. [XML Parser](./docs/v4/2.XMLparseOptions.md)
|
|
108
117
|
3. [XML Builder](./docs/v4/3.XMLBuilder.md)
|
|
109
118
|
4. [XML Validator](./docs/v4/4.XMLValidator.md)
|
|
110
|
-
5. [Entites](./docs/5.Entities.md)
|
|
111
|
-
6. [HTML Document Parsing](./docs/6.HTMLParsing.md)
|
|
112
|
-
7. [PI Tag processing](./docs/7.PITags.md)
|
|
119
|
+
5. [Entites](./docs/v4/5.Entities.md)
|
|
120
|
+
6. [HTML Document Parsing](./docs/v4/6.HTMLParsing.md)
|
|
121
|
+
7. [PI Tag processing](./docs/v4/7.PITags.md)
|
|
113
122
|
## Performance
|
|
114
123
|
|
|
115
124
|
### XML Parser
|
|
@@ -135,6 +144,7 @@ In a HTML page
|
|
|
135
144
|
* Run tests for a route or from a route
|
|
136
145
|
* Customizable reporting
|
|
137
146
|
* Central dashboard for better monitoring
|
|
147
|
+
* Options to integrate E2E tests with Jira, Github etc using Central dashboard `Tian`.
|
|
138
148
|
* **[Stubmatic](https://github.com/NaturalIntelligence/Stubmatic)** : Create fake webservices, DynamoDB or S3 servers, Manage fake/mock stub data, Or fake any HTTP(s) call.
|
|
139
149
|
|
|
140
150
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-xml-parser",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "Validate XML, Parse XML, Build XML without C/C++ based libraries",
|
|
5
5
|
"main": "./src/fxp.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"coverage": "nyc report --reporter html --reporter text -t .nyc_output --report-dir .nyc_output/summary",
|
|
10
10
|
"perf": "node ./benchmark/perfTest3.js",
|
|
11
11
|
"lint": "eslint src/*.js spec/*.js",
|
|
12
|
-
"bundle": "webpack
|
|
12
|
+
"bundle": "webpack --config webpack-prod.config.js",
|
|
13
13
|
"prettier": "prettier --write src/**/*.js",
|
|
14
14
|
"publish-please": "publish-please",
|
|
15
15
|
"checkReadiness": "publish-please --dry-run"
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"@babel/preset-env": "^7.13.10",
|
|
48
48
|
"@babel/register": "^7.13.8",
|
|
49
49
|
"babel-loader": "^8.2.2",
|
|
50
|
+
"cytorus": "^0.2.9",
|
|
50
51
|
"eslint": "^8.3.0",
|
|
51
52
|
"he": "^1.2.0",
|
|
52
53
|
"jasmine": "^3.6.4",
|
package/src/fxp_cjs
ADDED
package/src/util.js
CHANGED
|
@@ -67,23 +67,6 @@ exports.getValue = function(v) {
|
|
|
67
67
|
// const fakeCall = function(a) {return a;};
|
|
68
68
|
// const fakeCallNoReturn = function() {};
|
|
69
69
|
|
|
70
|
-
const buildOptions = function(options, defaultOptions, props) {
|
|
71
|
-
let newOptions = {};
|
|
72
|
-
if (!options) {
|
|
73
|
-
return defaultOptions; //if there are not options
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
for (let i = 0; i < props.length; i++) {
|
|
77
|
-
if (options[props[i]] !== undefined) {
|
|
78
|
-
newOptions[props[i]] = options[props[i]];
|
|
79
|
-
} else {
|
|
80
|
-
newOptions[props[i]] = defaultOptions[props[i]];
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return newOptions;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
exports.buildOptions = buildOptions;
|
|
87
70
|
exports.isName = isName;
|
|
88
71
|
exports.getAllMatches = getAllMatches;
|
|
89
72
|
exports.nameRegexp = nameRegexp;
|
package/src/validator.js
CHANGED
|
@@ -7,14 +7,9 @@ const defaultOptions = {
|
|
|
7
7
|
unpairedTags: []
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
const props = [
|
|
11
|
-
'allowBooleanAttributes',
|
|
12
|
-
'unpairedTags'
|
|
13
|
-
];
|
|
14
|
-
|
|
15
10
|
//const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g");
|
|
16
11
|
exports.validate = function (xmlData, options) {
|
|
17
|
-
options =
|
|
12
|
+
options = Object.assign({}, defaultOptions, options);
|
|
18
13
|
|
|
19
14
|
//xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line
|
|
20
15
|
//xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag
|
|
@@ -331,6 +326,8 @@ function validateAttributeString(attrStr, options) {
|
|
|
331
326
|
if (matches[i][1].length === 0) {
|
|
332
327
|
//nospace before attribute name: a="sd"b="saf"
|
|
333
328
|
return getErrorObject('InvalidAttr', "Attribute '"+matches[i][2]+"' has no space in starting.", getPositionFromMatch(matches[i]))
|
|
329
|
+
} else if (matches[i][3] !== undefined && matches[i][4] === undefined) {
|
|
330
|
+
return getErrorObject('InvalidAttr', "Attribute '"+matches[i][2]+"' is without value.", getPositionFromMatch(matches[i]));
|
|
334
331
|
} else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {
|
|
335
332
|
//independent attribute: ab
|
|
336
333
|
return getErrorObject('InvalidAttr', "boolean attribute '"+matches[i][2]+"' is not allowed.", getPositionFromMatch(matches[i]));
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
//parse Empty Node as self closing node
|
|
3
|
-
const buildOptions = require('../util').buildOptions;
|
|
4
3
|
const buildFromOrderedJs = require('./orderedJs2Xml');
|
|
5
4
|
|
|
6
5
|
const defaultOptions = {
|
|
@@ -32,30 +31,8 @@ const defaultOptions = {
|
|
|
32
31
|
stopNodes: []
|
|
33
32
|
};
|
|
34
33
|
|
|
35
|
-
const props = [
|
|
36
|
-
'attributeNamePrefix',
|
|
37
|
-
'attributesGroupName',
|
|
38
|
-
'textNodeName',
|
|
39
|
-
'ignoreAttributes',
|
|
40
|
-
'cdataPropName',
|
|
41
|
-
'format',
|
|
42
|
-
'indentBy',
|
|
43
|
-
'suppressEmptyNode',
|
|
44
|
-
'suppressBooleanAttributes',
|
|
45
|
-
'tagValueProcessor',
|
|
46
|
-
'attributeValueProcessor',
|
|
47
|
-
'arrayNodeName', //when array as root
|
|
48
|
-
'preserveOrder',
|
|
49
|
-
"commentPropName",
|
|
50
|
-
"unpairedTags",
|
|
51
|
-
"entities",
|
|
52
|
-
"processEntities",
|
|
53
|
-
"stopNodes",
|
|
54
|
-
// 'rootNodeName', //when jsObject have multiple properties on root level
|
|
55
|
-
];
|
|
56
|
-
|
|
57
34
|
function Builder(options) {
|
|
58
|
-
this.options =
|
|
35
|
+
this.options = Object.assign({}, defaultOptions, options);
|
|
59
36
|
if (this.options.ignoreAttributes || this.options.attributesGroupName) {
|
|
60
37
|
this.isAttribute = function(/*a*/) {
|
|
61
38
|
return false;
|
|
@@ -91,6 +68,7 @@ function Builder(options) {
|
|
|
91
68
|
this.buildObjectNode = buildObjectNode;
|
|
92
69
|
|
|
93
70
|
this.replaceEntitiesValue = replaceEntitiesValue;
|
|
71
|
+
this.buildAttrPairStr = buildAttrPairStr;
|
|
94
72
|
}
|
|
95
73
|
|
|
96
74
|
Builder.prototype.build = function(jObj) {
|
|
@@ -113,16 +91,16 @@ Builder.prototype.j2x = function(jObj, level) {
|
|
|
113
91
|
if (typeof jObj[key] === 'undefined') {
|
|
114
92
|
// supress undefined node
|
|
115
93
|
} else if (jObj[key] === null) {
|
|
116
|
-
val += this.indentate(level) + '<' + key + '
|
|
94
|
+
if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
|
|
95
|
+
else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
96
|
+
// val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
117
97
|
} else if (jObj[key] instanceof Date) {
|
|
118
98
|
val += this.buildTextNode(jObj[key], key, '', level);
|
|
119
99
|
} else if (typeof jObj[key] !== 'object') {
|
|
120
100
|
//premitive type
|
|
121
101
|
const attr = this.isAttribute(key);
|
|
122
102
|
if (attr) {
|
|
123
|
-
|
|
124
|
-
val = this.replaceEntitiesValue(val);
|
|
125
|
-
attrStr += ' ' + attr + '="' + val + '"';
|
|
103
|
+
attrStr += this.buildAttrPairStr(attr, '' + jObj[key]);
|
|
126
104
|
}else {
|
|
127
105
|
//tag value
|
|
128
106
|
if (key === this.options.textNodeName) {
|
|
@@ -140,7 +118,9 @@ Builder.prototype.j2x = function(jObj, level) {
|
|
|
140
118
|
if (typeof item === 'undefined') {
|
|
141
119
|
// supress undefined node
|
|
142
120
|
} else if (item === null) {
|
|
143
|
-
val += this.indentate(level) + '<' + key + '
|
|
121
|
+
if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
|
|
122
|
+
else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
123
|
+
// val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
144
124
|
} else if (typeof item === 'object') {
|
|
145
125
|
val += this.processTextOrObjNode(item, key, level)
|
|
146
126
|
} else {
|
|
@@ -153,9 +133,7 @@ Builder.prototype.j2x = function(jObj, level) {
|
|
|
153
133
|
const Ks = Object.keys(jObj[key]);
|
|
154
134
|
const L = Ks.length;
|
|
155
135
|
for (let j = 0; j < L; j++) {
|
|
156
|
-
|
|
157
|
-
val = this.replaceEntitiesValue(val);
|
|
158
|
-
attrStr += ' ' + Ks[j] + '="' + val + '"';
|
|
136
|
+
attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]);
|
|
159
137
|
}
|
|
160
138
|
} else {
|
|
161
139
|
val += this.processTextOrObjNode(jObj[key], key, level)
|
|
@@ -165,6 +143,14 @@ Builder.prototype.j2x = function(jObj, level) {
|
|
|
165
143
|
return {attrStr: attrStr, val: val};
|
|
166
144
|
};
|
|
167
145
|
|
|
146
|
+
function buildAttrPairStr(attrName, val){
|
|
147
|
+
val = this.options.attributeValueProcessor(attrName, '' + val);
|
|
148
|
+
val = this.replaceEntitiesValue(val);
|
|
149
|
+
if (this.options.suppressBooleanAttributes && val === "true") {
|
|
150
|
+
return ' ' + attrName;
|
|
151
|
+
} else return ' ' + attrName + '="' + val + '"';
|
|
152
|
+
}
|
|
153
|
+
|
|
168
154
|
function processTextOrObjNode (object, key, level) {
|
|
169
155
|
const result = this.j2x(object, level + 1);
|
|
170
156
|
if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
|
|
@@ -175,34 +161,24 @@ function processTextOrObjNode (object, key, level) {
|
|
|
175
161
|
}
|
|
176
162
|
|
|
177
163
|
function buildObjectNode(val, key, attrStr, level) {
|
|
164
|
+
let tagEndExp = '</' + key + this.tagEndChar;
|
|
165
|
+
let piClosingChar = "";
|
|
166
|
+
|
|
167
|
+
if(key[0] === "?") {
|
|
168
|
+
piClosingChar = "?";
|
|
169
|
+
tagEndExp = "";
|
|
170
|
+
}
|
|
171
|
+
|
|
178
172
|
if (attrStr && val.indexOf('<') === -1) {
|
|
179
173
|
return (
|
|
180
|
-
this.indentate(level) +
|
|
181
|
-
'<' +
|
|
182
|
-
key +
|
|
183
|
-
attrStr +
|
|
184
|
-
'>' +
|
|
174
|
+
this.indentate(level) + '<' + key + attrStr + piClosingChar + '>' +
|
|
185
175
|
val +
|
|
186
|
-
|
|
187
|
-
// + this.indentate(level)
|
|
188
|
-
'</' +
|
|
189
|
-
key +
|
|
190
|
-
this.tagEndChar
|
|
191
|
-
);
|
|
176
|
+
tagEndExp );
|
|
192
177
|
} else {
|
|
193
178
|
return (
|
|
194
|
-
this.indentate(level) +
|
|
195
|
-
'<' +
|
|
196
|
-
key +
|
|
197
|
-
attrStr +
|
|
198
|
-
this.tagEndChar +
|
|
179
|
+
this.indentate(level) + '<' + key + attrStr + piClosingChar + this.tagEndChar +
|
|
199
180
|
val +
|
|
200
|
-
|
|
201
|
-
this.indentate(level) +
|
|
202
|
-
'</' +
|
|
203
|
-
key +
|
|
204
|
-
this.tagEndChar
|
|
205
|
-
);
|
|
181
|
+
this.indentate(level) + tagEndExp );
|
|
206
182
|
}
|
|
207
183
|
}
|
|
208
184
|
|
|
@@ -210,8 +186,8 @@ function buildEmptyObjNode(val, key, attrStr, level) {
|
|
|
210
186
|
if (val !== '') {
|
|
211
187
|
return this.buildObjectNode(val, key, attrStr, level);
|
|
212
188
|
} else {
|
|
213
|
-
return
|
|
214
|
-
|
|
189
|
+
if(key[0] === "?") return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
|
|
190
|
+
else return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
|
|
215
191
|
}
|
|
216
192
|
}
|
|
217
193
|
|
|
@@ -220,16 +196,9 @@ function buildTextValNode(val, key, attrStr, level) {
|
|
|
220
196
|
textValue = this.replaceEntitiesValue(textValue);
|
|
221
197
|
|
|
222
198
|
return (
|
|
223
|
-
this.indentate(level) +
|
|
224
|
-
'<' +
|
|
225
|
-
key +
|
|
226
|
-
attrStr +
|
|
227
|
-
'>' +
|
|
199
|
+
this.indentate(level) + '<' + key + attrStr + '>' +
|
|
228
200
|
textValue +
|
|
229
|
-
'</' +
|
|
230
|
-
key +
|
|
231
|
-
this.tagEndChar
|
|
232
|
-
);
|
|
201
|
+
'</' + key + this.tagEndChar );
|
|
233
202
|
}
|
|
234
203
|
|
|
235
204
|
function replaceEntitiesValue(textValue){
|
|
@@ -248,7 +217,8 @@ function buildEmptyTextNode(val, key, attrStr, level) {
|
|
|
248
217
|
}else if (val !== '') {
|
|
249
218
|
return this.buildTextValNode(val, key, attrStr, level);
|
|
250
219
|
} else {
|
|
251
|
-
return
|
|
220
|
+
if(key[0] === "?") return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
|
|
221
|
+
else return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
|
|
252
222
|
}
|
|
253
223
|
}
|
|
254
224
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const EOL = "\n";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
@@ -47,12 +47,10 @@ function arrToStr(arr, options, jPath, level){
|
|
|
47
47
|
const attStr = attr_to_str(tagObj[":@"], options);
|
|
48
48
|
let tagStart = indentation + `<${tagName}${attStr}`;
|
|
49
49
|
let tagValue = arrToStr(tagObj[tagName], options, newJPath, level + 1);
|
|
50
|
-
if(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
xmlStr += tagStart + "/>";
|
|
55
|
-
}
|
|
50
|
+
if(options.unpairedTags.indexOf(tagName) !== -1){
|
|
51
|
+
xmlStr += tagStart + ">";
|
|
52
|
+
}else if( (!tagValue || tagValue.length === 0) && options.suppressEmptyNode){
|
|
53
|
+
xmlStr += tagStart + "/>";
|
|
56
54
|
}else{
|
|
57
55
|
//TODO: node with only text value should not parse the text value in next line
|
|
58
56
|
xmlStr += tagStart + `>${tagValue}${indentation}</${tagName}>` ;
|
|
@@ -31,36 +31,9 @@ const defaultOptions = {
|
|
|
31
31
|
htmlEntities: false,
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
const props = [
|
|
35
|
-
'preserveOrder',
|
|
36
|
-
'attributeNamePrefix',
|
|
37
|
-
'attributesGroupName',
|
|
38
|
-
'textNodeName',
|
|
39
|
-
'ignoreAttributes',
|
|
40
|
-
'removeNSPrefix',
|
|
41
|
-
'allowBooleanAttributes',
|
|
42
|
-
'parseTagValue',
|
|
43
|
-
'parseAttributeValue',
|
|
44
|
-
'trimValues',
|
|
45
|
-
'cdataPropName',
|
|
46
|
-
'tagValueProcessor',
|
|
47
|
-
'attributeValueProcessor',
|
|
48
|
-
'numberParseOptions',
|
|
49
|
-
'stopNodes',
|
|
50
|
-
'alwaysCreateTextNode',
|
|
51
|
-
'isArray',
|
|
52
|
-
'commentPropName',
|
|
53
|
-
'unpairedTags',
|
|
54
|
-
'processEntities',
|
|
55
|
-
'htmlEntities'
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
const util = require('../util');
|
|
59
|
-
|
|
60
34
|
const buildOptions = function(options) {
|
|
61
|
-
return
|
|
35
|
+
return Object.assign({}, defaultOptions, options);
|
|
62
36
|
};
|
|
63
37
|
|
|
64
38
|
exports.buildOptions = buildOptions;
|
|
65
|
-
exports.defaultOptions = defaultOptions;
|
|
66
|
-
exports.props = props;
|
|
39
|
+
exports.defaultOptions = defaultOptions;
|
|
@@ -49,6 +49,7 @@ class OrderedObjParser{
|
|
|
49
49
|
this.isItStopNode = isItStopNode;
|
|
50
50
|
this.replaceEntitiesValue = replaceEntitiesValue;
|
|
51
51
|
this.readStopNodeData = readStopNodeData;
|
|
52
|
+
this.saveTextToParentTag = saveTextToParentTag;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
}
|
|
@@ -71,14 +72,15 @@ function addExternalEntities(externalEntities){
|
|
|
71
72
|
* @param {boolean} dontTrim
|
|
72
73
|
* @param {boolean} hasAttributes
|
|
73
74
|
* @param {boolean} isLeafNode
|
|
75
|
+
* @param {boolean} escapeEntities
|
|
74
76
|
*/
|
|
75
|
-
function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode) {
|
|
77
|
+
function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {
|
|
76
78
|
if (val !== undefined) {
|
|
77
79
|
if (this.options.trimValues && !dontTrim) {
|
|
78
80
|
val = val.trim();
|
|
79
81
|
}
|
|
80
82
|
if(val.length > 0){
|
|
81
|
-
val = this.replaceEntitiesValue(val);
|
|
83
|
+
if(!escapeEntities) val = this.replaceEntitiesValue(val);
|
|
82
84
|
|
|
83
85
|
const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode);
|
|
84
86
|
if(newval === null || newval === undefined){
|
|
@@ -192,14 +194,7 @@ const parseXml = function(xmlData) {
|
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
if(currentNode){
|
|
195
|
-
textData = this.
|
|
196
|
-
, currentNode.tagname
|
|
197
|
-
, jPath
|
|
198
|
-
,false
|
|
199
|
-
, currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false
|
|
200
|
-
, Object.keys(currentNode.child).length === 0);
|
|
201
|
-
if(textData !== undefined && textData !== "") currentNode.add(this.options.textNodeName, textData);
|
|
202
|
-
textData = "";
|
|
197
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
203
198
|
}
|
|
204
199
|
|
|
205
200
|
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
@@ -208,53 +203,26 @@ const parseXml = function(xmlData) {
|
|
|
208
203
|
textData = "";
|
|
209
204
|
i = closeIndex;
|
|
210
205
|
} else if( xmlData[i+1] === '?') {
|
|
211
|
-
let
|
|
212
|
-
if(!
|
|
213
|
-
|
|
214
|
-
let tagName= result.tagName;
|
|
215
|
-
let tagExp = result.tagExp;
|
|
216
|
-
let attrExpPresent = result.attrExpPresent;
|
|
217
|
-
let closeIndex = result.closeIndex;
|
|
206
|
+
let tagData = readTagExp(xmlData,i, false, "?>");
|
|
207
|
+
if(!tagData) throw new Error("Pi Tag is not closed.");
|
|
208
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
218
209
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
, Object.keys(currentNode.child).length === 0);
|
|
227
|
-
|
|
228
|
-
if(textData !== undefined && textData !== "") currentNode.add(this.options.textNodeName, textData);
|
|
229
|
-
textData = "";
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const childNode = new xmlNode(tagName);
|
|
233
|
-
childNode.add(this.options.textNodeName, "");
|
|
234
|
-
|
|
235
|
-
if(tagName !== tagExp && attrExpPresent){
|
|
236
|
-
childNode[":@"] = this.buildAttributesMap(tagExp, jPath);
|
|
237
|
-
}
|
|
238
|
-
currentNode.addChild(childNode);
|
|
210
|
+
const childNode = new xmlNode(tagData.tagName);
|
|
211
|
+
childNode.add(this.options.textNodeName, "");
|
|
212
|
+
|
|
213
|
+
if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
|
|
214
|
+
childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath);
|
|
215
|
+
}
|
|
216
|
+
currentNode.addChild(childNode);
|
|
239
217
|
|
|
240
|
-
i = closeIndex + 1;
|
|
218
|
+
i = tagData.closeIndex + 1;
|
|
241
219
|
} else if(xmlData.substr(i + 1, 3) === '!--') {
|
|
242
220
|
const endIndex = findClosingIndex(xmlData, "-->", i, "Comment is not closed.")
|
|
243
221
|
if(this.options.commentPropName){
|
|
244
222
|
const comment = xmlData.substring(i + 4, endIndex - 2);
|
|
245
223
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
textData = this.parseTextData(textData
|
|
249
|
-
, currentNode.tagname
|
|
250
|
-
, jPath
|
|
251
|
-
,false
|
|
252
|
-
, currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false
|
|
253
|
-
, Object.keys(currentNode.child).length === 0);
|
|
254
|
-
|
|
255
|
-
if(textData !== undefined && textData !== "") currentNode.add(this.options.textNodeName, textData);
|
|
256
|
-
textData = "";
|
|
257
|
-
}
|
|
224
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
225
|
+
|
|
258
226
|
currentNode.add(this.options.commentPropName, [ { [this.options.textNodeName] : comment } ]);
|
|
259
227
|
}
|
|
260
228
|
i = endIndex;
|
|
@@ -266,17 +234,7 @@ const parseXml = function(xmlData) {
|
|
|
266
234
|
const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
|
|
267
235
|
const tagExp = xmlData.substring(i + 9,closeIndex);
|
|
268
236
|
|
|
269
|
-
|
|
270
|
-
textData = this.parseTextData(textData
|
|
271
|
-
, currentNode.tagname
|
|
272
|
-
, jPath
|
|
273
|
-
,false
|
|
274
|
-
, currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false
|
|
275
|
-
, Object.keys(currentNode.child).length === 0);
|
|
276
|
-
|
|
277
|
-
if(textData !== undefined && textData !== "") currentNode.add(this.options.textNodeName, textData);
|
|
278
|
-
textData = "";
|
|
279
|
-
}
|
|
237
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
280
238
|
|
|
281
239
|
//cdata should be set even if it is 0 length string
|
|
282
240
|
if(this.options.cdataPropName){
|
|
@@ -302,14 +260,7 @@ const parseXml = function(xmlData) {
|
|
|
302
260
|
if (currentNode && textData) {
|
|
303
261
|
if(currentNode.tagname !== '!xml'){
|
|
304
262
|
//when nested tag is found
|
|
305
|
-
textData = this.
|
|
306
|
-
, currentNode.tagname
|
|
307
|
-
, jPath
|
|
308
|
-
, false
|
|
309
|
-
, currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false
|
|
310
|
-
, false);
|
|
311
|
-
if(textData !== undefined && textData !== "") currentNode.add(this.options.textNodeName, textData);
|
|
312
|
-
textData = "";
|
|
263
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath, false);
|
|
313
264
|
}
|
|
314
265
|
}
|
|
315
266
|
|
|
@@ -342,6 +293,10 @@ const parseXml = function(xmlData) {
|
|
|
342
293
|
if(tagName !== tagExp && attrExpPresent){
|
|
343
294
|
childNode[":@"] = this.buildAttributesMap(tagExp, jPath);
|
|
344
295
|
}
|
|
296
|
+
if(tagContent) {
|
|
297
|
+
tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
|
|
298
|
+
}
|
|
299
|
+
|
|
345
300
|
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
346
301
|
childNode.add(this.options.textNodeName, tagContent);
|
|
347
302
|
|
|
@@ -405,6 +360,24 @@ const replaceEntitiesValue = function(val){
|
|
|
405
360
|
}
|
|
406
361
|
return val;
|
|
407
362
|
}
|
|
363
|
+
function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) {
|
|
364
|
+
if (textData) { //store previously collected data as textNode
|
|
365
|
+
if(isLeafNode === undefined) isLeafNode = Object.keys(currentNode.child).length === 0
|
|
366
|
+
|
|
367
|
+
textData = this.parseTextData(textData,
|
|
368
|
+
currentNode.tagname,
|
|
369
|
+
jPath,
|
|
370
|
+
false,
|
|
371
|
+
currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false,
|
|
372
|
+
isLeafNode);
|
|
373
|
+
|
|
374
|
+
if (textData !== undefined && textData !== "")
|
|
375
|
+
currentNode.add(this.options.textNodeName, textData);
|
|
376
|
+
textData = "";
|
|
377
|
+
}
|
|
378
|
+
return textData;
|
|
379
|
+
}
|
|
380
|
+
|
|
408
381
|
//TODO: use jPath to simplify the logic
|
|
409
382
|
/**
|
|
410
383
|
*
|