fast-xml-parser 4.0.0-beta.5 → 4.0.0
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 +21 -3
- package/README.md +14 -1
- package/package.json +3 -2
- package/src/fxp.d.ts +1 -0
- package/src/fxp_cjs +8 -0
- package/src/util.js +0 -17
- package/src/validator.js +3 -6
- package/src/xmlbuilder/json2xml.js +2 -23
- package/src/xmlbuilder/orderedJs2Xml.js +14 -8
- package/src/xmlparser/OptionsBuilder.js +2 -29
- package/src/xmlparser/OrderedObjParser.js +61 -49
package/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,33 @@
|
|
|
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.0 / 2022-01-06**
|
|
4
|
+
* Generating different combined, parser only, builder only, validator only browser bundles
|
|
5
|
+
* Keeping cjs modules as they can be imported in cjs and esm modules both. Otherwise refer `esm` branch.
|
|
6
|
+
|
|
7
|
+
**4.0.0-beta.8 / 2021-12-13**
|
|
8
|
+
* call tagValueProcessor for stop nodes
|
|
9
|
+
|
|
10
|
+
**4.0.0-beta.7 / 2021-12-09**
|
|
11
|
+
* fix Validator bug when an attribute has no value but '=' only
|
|
12
|
+
* XML Builder should suppress unpaired tags by default.
|
|
13
|
+
* documents update for missing features
|
|
14
|
+
* refactoring to use Object.assign
|
|
15
|
+
* refactoring to remove repeated code
|
|
16
|
+
|
|
17
|
+
**4.0.0-beta.6 / 2021-12-05**
|
|
18
|
+
* Support PI Tags processing
|
|
19
|
+
* Support `suppressBooleanAttributes` by XML Builder for attributes with value `true`.
|
|
20
|
+
|
|
21
|
+
**4.0.0-beta.5 / 2021-12-04**
|
|
4
22
|
* fix: when a tag with name "attributes"
|
|
5
23
|
|
|
6
|
-
**
|
|
24
|
+
**4.0.0-beta.4 / 2021-12-02**
|
|
7
25
|
* Support HTML document parsing
|
|
8
26
|
* skip stop nodes parsing when building the XML from JS object
|
|
9
27
|
* Support external entites without DOCTYPE
|
|
10
28
|
* update dev dependency: strnum v1.0.5 to fix long number issue
|
|
11
29
|
|
|
12
|
-
**
|
|
30
|
+
**4.0.0-beta.3 / 2021-11-30**
|
|
13
31
|
* support global stopNodes expression like "*.stop"
|
|
14
32
|
* support self-closing and paired unpaired tags
|
|
15
33
|
* fix: CDATA should not be parsed.
|
package/README.md
CHANGED
|
@@ -55,6 +55,7 @@ Check the list of all known users [here](./USERs.md);
|
|
|
55
55
|
* Supports comments
|
|
56
56
|
* It can preserve Order of tags in JS object
|
|
57
57
|
* You can control if a single tag should be parsed into array.
|
|
58
|
+
* Supports parsing of PI (Processing Instruction) tags with XML declaration tags
|
|
58
59
|
* And many more other features.
|
|
59
60
|
|
|
60
61
|
## How to use
|
|
@@ -89,7 +90,7 @@ const xmlContent = builder.build(jObj);
|
|
|
89
90
|
|
|
90
91
|
In a HTML page
|
|
91
92
|
```html
|
|
92
|
-
<script src="path/to/
|
|
93
|
+
<script src="path/to/fxp.min.js"></script>
|
|
93
94
|
:
|
|
94
95
|
<script>
|
|
95
96
|
const parser = new fxparser.XMLParser();
|
|
@@ -97,6 +98,16 @@ In a HTML page
|
|
|
97
98
|
</script>
|
|
98
99
|
```
|
|
99
100
|
|
|
101
|
+
Check lib folder for different browser bundles
|
|
102
|
+
|
|
103
|
+
| Bundle Name | Size |
|
|
104
|
+
| -- | -- |
|
|
105
|
+
| fxbuilder.min.js | 5.2K |
|
|
106
|
+
| fxparser.js | 50K |
|
|
107
|
+
| fxparser.min.js | 17K |
|
|
108
|
+
| fxp.min.js | 22K |
|
|
109
|
+
| fxvalidator.min.js | 5.7K |
|
|
110
|
+
|
|
100
111
|
### Documents
|
|
101
112
|
**v3**
|
|
102
113
|
* [documents](./docs/v3/docs.md)
|
|
@@ -108,6 +119,7 @@ In a HTML page
|
|
|
108
119
|
4. [XML Validator](./docs/v4/4.XMLValidator.md)
|
|
109
120
|
5. [Entites](./docs/5.Entities.md)
|
|
110
121
|
6. [HTML Document Parsing](./docs/6.HTMLParsing.md)
|
|
122
|
+
7. [PI Tag processing](./docs/7.PITags.md)
|
|
111
123
|
## Performance
|
|
112
124
|
|
|
113
125
|
### XML Parser
|
|
@@ -133,6 +145,7 @@ In a HTML page
|
|
|
133
145
|
* Run tests for a route or from a route
|
|
134
146
|
* Customizable reporting
|
|
135
147
|
* Central dashboard for better monitoring
|
|
148
|
+
* Options to integrate E2E tests with Jira, Github etc using Central dashboard `Tian`.
|
|
136
149
|
* **[Stubmatic](https://github.com/NaturalIntelligence/Stubmatic)** : Create fake webservices, DynamoDB or S3 servers, Manage fake/mock stub data, Or fake any HTTP(s) call.
|
|
137
150
|
|
|
138
151
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-xml-parser",
|
|
3
|
-
"version": "4.0.0
|
|
3
|
+
"version": "4.0.0",
|
|
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.d.ts
CHANGED
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 = {
|
|
@@ -12,6 +11,7 @@ const defaultOptions = {
|
|
|
12
11
|
format: false,
|
|
13
12
|
indentBy: ' ',
|
|
14
13
|
suppressEmptyNode: false,
|
|
14
|
+
suppressBooleanAttributes: true,
|
|
15
15
|
tagValueProcessor: function(key, a) {
|
|
16
16
|
return a;
|
|
17
17
|
},
|
|
@@ -31,29 +31,8 @@ const defaultOptions = {
|
|
|
31
31
|
stopNodes: []
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
const props = [
|
|
35
|
-
'attributeNamePrefix',
|
|
36
|
-
'attributesGroupName',
|
|
37
|
-
'textNodeName',
|
|
38
|
-
'ignoreAttributes',
|
|
39
|
-
'cdataPropName',
|
|
40
|
-
'format',
|
|
41
|
-
'indentBy',
|
|
42
|
-
'suppressEmptyNode',
|
|
43
|
-
'tagValueProcessor',
|
|
44
|
-
'attributeValueProcessor',
|
|
45
|
-
'arrayNodeName', //when array as root
|
|
46
|
-
'preserveOrder',
|
|
47
|
-
"commentPropName",
|
|
48
|
-
"unpairedTags",
|
|
49
|
-
"entities",
|
|
50
|
-
"processEntities",
|
|
51
|
-
"stopNodes",
|
|
52
|
-
// 'rootNodeName', //when jsObject have multiple properties on root level
|
|
53
|
-
];
|
|
54
|
-
|
|
55
34
|
function Builder(options) {
|
|
56
|
-
this.options =
|
|
35
|
+
this.options = Object.assign({}, defaultOptions, options);
|
|
57
36
|
if (this.options.ignoreAttributes || this.options.attributesGroupName) {
|
|
58
37
|
this.isAttribute = function(/*a*/) {
|
|
59
38
|
return false;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const EOL = "\n";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
@@ -39,16 +39,18 @@ function arrToStr(arr, options, jPath, level){
|
|
|
39
39
|
}else if( tagName === options.commentPropName){
|
|
40
40
|
xmlStr += indentation + `<!--${tagObj[tagName][0][options.textNodeName]}-->`;
|
|
41
41
|
continue;
|
|
42
|
+
}else if( tagName[0] === "?"){
|
|
43
|
+
const attStr = attr_to_str(tagObj[":@"], options);
|
|
44
|
+
xmlStr += indentation + `<${tagName} ${tagObj[tagName][0][options.textNodeName]} ${attStr}?>`;
|
|
45
|
+
continue;
|
|
42
46
|
}
|
|
43
47
|
const attStr = attr_to_str(tagObj[":@"], options);
|
|
44
48
|
let tagStart = indentation + `<${tagName}${attStr}`;
|
|
45
49
|
let tagValue = arrToStr(tagObj[tagName], options, newJPath, level + 1);
|
|
46
|
-
if(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
xmlStr += tagStart + "/>";
|
|
51
|
-
}
|
|
50
|
+
if(options.unpairedTags.indexOf(tagName) !== -1){
|
|
51
|
+
xmlStr += tagStart + ">";
|
|
52
|
+
}else if( (!tagValue || tagValue.length === 0) && options.suppressEmptyNode){
|
|
53
|
+
xmlStr += tagStart + "/>";
|
|
52
54
|
}else{
|
|
53
55
|
//TODO: node with only text value should not parse the text value in next line
|
|
54
56
|
xmlStr += tagStart + `>${tagValue}${indentation}</${tagName}>` ;
|
|
@@ -72,7 +74,11 @@ function attr_to_str(attrMap, options){
|
|
|
72
74
|
for( attr in attrMap){
|
|
73
75
|
let attrVal = options.attributeValueProcessor(attr, attrMap[attr]);
|
|
74
76
|
attrVal = replaceEntitiesValue(attrVal, options);
|
|
75
|
-
|
|
77
|
+
if(attrVal === true && options.suppressBooleanAttributes){
|
|
78
|
+
attrStr+= ` ${attr.substr(options.attributeNamePrefix.length)}`;
|
|
79
|
+
}else{
|
|
80
|
+
attrStr+= ` ${attr.substr(options.attributeNamePrefix.length)}="${attrVal}"`;
|
|
81
|
+
}
|
|
76
82
|
}
|
|
77
83
|
}
|
|
78
84
|
return attrStr;
|
|
@@ -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;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
///@ts-check
|
|
2
3
|
|
|
3
4
|
const util = require('../util');
|
|
4
5
|
const xmlNode = require('./xmlNode');
|
|
@@ -47,8 +48,8 @@ class OrderedObjParser{
|
|
|
47
48
|
this.buildAttributesMap = buildAttributesMap;
|
|
48
49
|
this.isItStopNode = isItStopNode;
|
|
49
50
|
this.replaceEntitiesValue = replaceEntitiesValue;
|
|
50
|
-
this.readTagExp = readTagExp;
|
|
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,24 +203,26 @@ const parseXml = function(xmlData) {
|
|
|
208
203
|
textData = "";
|
|
209
204
|
i = closeIndex;
|
|
210
205
|
} else if( xmlData[i+1] === '?') {
|
|
211
|
-
|
|
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);
|
|
209
|
+
|
|
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);
|
|
217
|
+
|
|
218
|
+
i = tagData.closeIndex + 1;
|
|
212
219
|
} else if(xmlData.substr(i + 1, 3) === '!--') {
|
|
213
220
|
const endIndex = findClosingIndex(xmlData, "-->", i, "Comment is not closed.")
|
|
214
221
|
if(this.options.commentPropName){
|
|
215
222
|
const comment = xmlData.substring(i + 4, endIndex - 2);
|
|
216
223
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
textData = this.parseTextData(textData
|
|
220
|
-
, currentNode.tagname
|
|
221
|
-
, jPath
|
|
222
|
-
,false
|
|
223
|
-
, currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false
|
|
224
|
-
, Object.keys(currentNode.child).length === 0);
|
|
225
|
-
|
|
226
|
-
if(textData !== undefined && textData !== "") currentNode.add(this.options.textNodeName, textData);
|
|
227
|
-
textData = "";
|
|
228
|
-
}
|
|
224
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
225
|
+
|
|
229
226
|
currentNode.add(this.options.commentPropName, [ { [this.options.textNodeName] : comment } ]);
|
|
230
227
|
}
|
|
231
228
|
i = endIndex;
|
|
@@ -237,17 +234,7 @@ const parseXml = function(xmlData) {
|
|
|
237
234
|
const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
|
|
238
235
|
const tagExp = xmlData.substring(i + 9,closeIndex);
|
|
239
236
|
|
|
240
|
-
|
|
241
|
-
textData = this.parseTextData(textData
|
|
242
|
-
, currentNode.tagname
|
|
243
|
-
, jPath
|
|
244
|
-
,false
|
|
245
|
-
, currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false
|
|
246
|
-
, Object.keys(currentNode.child).length === 0);
|
|
247
|
-
|
|
248
|
-
if(textData !== undefined && textData !== "") currentNode.add(this.options.textNodeName, textData);
|
|
249
|
-
textData = "";
|
|
250
|
-
}
|
|
237
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
251
238
|
|
|
252
239
|
//cdata should be set even if it is 0 length string
|
|
253
240
|
if(this.options.cdataPropName){
|
|
@@ -263,7 +250,7 @@ const parseXml = function(xmlData) {
|
|
|
263
250
|
i = closeIndex + 2;
|
|
264
251
|
}else {//Opening tag
|
|
265
252
|
|
|
266
|
-
let result =
|
|
253
|
+
let result = readTagExp(xmlData,i, this. options.removeNSPrefix);
|
|
267
254
|
let tagName= result.tagName;
|
|
268
255
|
let tagExp = result.tagExp;
|
|
269
256
|
let attrExpPresent = result.attrExpPresent;
|
|
@@ -273,14 +260,7 @@ const parseXml = function(xmlData) {
|
|
|
273
260
|
if (currentNode && textData) {
|
|
274
261
|
if(currentNode.tagname !== '!xml'){
|
|
275
262
|
//when nested tag is found
|
|
276
|
-
textData = this.
|
|
277
|
-
, currentNode.tagname
|
|
278
|
-
, jPath
|
|
279
|
-
, false
|
|
280
|
-
, currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false
|
|
281
|
-
, false);
|
|
282
|
-
if(textData !== undefined && textData !== "") currentNode.add(this.options.textNodeName, textData);
|
|
283
|
-
textData = "";
|
|
263
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath, false);
|
|
284
264
|
}
|
|
285
265
|
}
|
|
286
266
|
|
|
@@ -313,6 +293,10 @@ const parseXml = function(xmlData) {
|
|
|
313
293
|
if(tagName !== tagExp && attrExpPresent){
|
|
314
294
|
childNode[":@"] = this.buildAttributesMap(tagExp, jPath);
|
|
315
295
|
}
|
|
296
|
+
if(tagContent) {
|
|
297
|
+
tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
|
|
298
|
+
}
|
|
299
|
+
|
|
316
300
|
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
317
301
|
childNode.add(this.options.textNodeName, tagContent);
|
|
318
302
|
|
|
@@ -376,6 +360,24 @@ const replaceEntitiesValue = function(val){
|
|
|
376
360
|
}
|
|
377
361
|
return val;
|
|
378
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
|
+
|
|
379
381
|
//TODO: use jPath to simplify the logic
|
|
380
382
|
/**
|
|
381
383
|
*
|
|
@@ -398,7 +400,7 @@ function isItStopNode(stopNodes, jPath, currentTagName){
|
|
|
398
400
|
* @param {number} i starting index
|
|
399
401
|
* @returns
|
|
400
402
|
*/
|
|
401
|
-
function tagExpWithClosingIndex(xmlData, i){
|
|
403
|
+
function tagExpWithClosingIndex(xmlData, i, closingChar = ">"){
|
|
402
404
|
let attrBoundary;
|
|
403
405
|
let tagExp = "";
|
|
404
406
|
for (let index = i; index < xmlData.length; index++) {
|
|
@@ -407,11 +409,20 @@ function tagExpWithClosingIndex(xmlData, i){
|
|
|
407
409
|
if (ch === attrBoundary) attrBoundary = "";//reset
|
|
408
410
|
} else if (ch === '"' || ch === "'") {
|
|
409
411
|
attrBoundary = ch;
|
|
410
|
-
} else if (ch ===
|
|
412
|
+
} else if (ch === closingChar[0]) {
|
|
413
|
+
if(closingChar[1]){
|
|
414
|
+
if(xmlData[index + 1] === closingChar[1]){
|
|
415
|
+
return {
|
|
416
|
+
data: tagExp,
|
|
417
|
+
index: index
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}else{
|
|
411
421
|
return {
|
|
412
422
|
data: tagExp,
|
|
413
423
|
index: index
|
|
414
424
|
}
|
|
425
|
+
}
|
|
415
426
|
} else if (ch === '\t') {
|
|
416
427
|
ch = " "
|
|
417
428
|
}
|
|
@@ -428,8 +439,9 @@ function findClosingIndex(xmlData, str, i, errMsg){
|
|
|
428
439
|
}
|
|
429
440
|
}
|
|
430
441
|
|
|
431
|
-
function readTagExp(xmlData,i){
|
|
432
|
-
const result = tagExpWithClosingIndex(xmlData, i+1);
|
|
442
|
+
function readTagExp(xmlData,i, removeNSPrefix, closingChar = ">"){
|
|
443
|
+
const result = tagExpWithClosingIndex(xmlData, i+1, closingChar);
|
|
444
|
+
if(!result) return;
|
|
433
445
|
let tagExp = result.data;
|
|
434
446
|
const closeIndex = result.index;
|
|
435
447
|
const separatorIndex = tagExp.search(/\s/);
|
|
@@ -440,7 +452,7 @@ function readTagExp(xmlData,i){
|
|
|
440
452
|
tagExp = tagExp.substr(separatorIndex + 1);
|
|
441
453
|
}
|
|
442
454
|
|
|
443
|
-
if(
|
|
455
|
+
if(removeNSPrefix){
|
|
444
456
|
const colonIndex = tagName.indexOf(":");
|
|
445
457
|
if(colonIndex !== -1){
|
|
446
458
|
tagName = tagName.substr(colonIndex+1);
|