fast-xml-parser 4.0.1 → 4.0.2
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
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
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
|
+
**4.0.2 / 2022-02-04**
|
|
4
|
+
* builder supports `suppressUnpairedNode`
|
|
5
|
+
* parser supports `ignoreDeclaration` and `ignorePiTags`
|
|
6
|
+
* fix: when comment is parsed as text value if given as `<!--> ...` #423
|
|
7
|
+
* builder supports decoding `&`
|
|
8
|
+
|
|
3
9
|
**4.0.1 / 2022-01-08**
|
|
4
10
|
* fix builder for pi tag
|
|
5
11
|
* fix: support suppressBooleanAttrs by builder
|
package/package.json
CHANGED
package/src/fxp.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ type X2jOptions = {
|
|
|
20
20
|
isArray: (tagName: string, jPath: string, isLeafNode: boolean, isAttribute: boolean) => boolean;
|
|
21
21
|
processEntities: boolean;
|
|
22
22
|
htmlEntities: boolean;
|
|
23
|
+
ignoreDeclaration: boolean;
|
|
24
|
+
ignorePiTags: boolean;
|
|
23
25
|
};
|
|
24
26
|
type strnumOptions = {
|
|
25
27
|
hex: boolean;
|
|
@@ -44,6 +46,7 @@ type XmlBuilderOptions = {
|
|
|
44
46
|
indentBy: string;
|
|
45
47
|
arrayNodeName: string;
|
|
46
48
|
suppressEmptyNode: boolean;
|
|
49
|
+
suppressUnpairedNode: boolean;
|
|
47
50
|
suppressBooleanAttributes: boolean;
|
|
48
51
|
preserveOrder: boolean;
|
|
49
52
|
unpairedTags: string[];
|
|
@@ -11,6 +11,7 @@ const defaultOptions = {
|
|
|
11
11
|
format: false,
|
|
12
12
|
indentBy: ' ',
|
|
13
13
|
suppressEmptyNode: false,
|
|
14
|
+
suppressUnpairedNode: true,
|
|
14
15
|
suppressBooleanAttributes: true,
|
|
15
16
|
tagValueProcessor: function(key, a) {
|
|
16
17
|
return a;
|
|
@@ -21,12 +22,13 @@ const defaultOptions = {
|
|
|
21
22
|
preserveOrder: false,
|
|
22
23
|
commentPropName: false,
|
|
23
24
|
unpairedTags: [],
|
|
24
|
-
entities:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
entities: [
|
|
26
|
+
{ regex: new RegExp("&", "g"), val: "&" },//it must be on top
|
|
27
|
+
{ regex: new RegExp(">", "g"), val: ">" },
|
|
28
|
+
{ regex: new RegExp("<", "g"), val: "<" },
|
|
29
|
+
{ regex: new RegExp("\'", "g"), val: "'" },
|
|
30
|
+
{ regex: new RegExp("\"", "g"), val: """ }
|
|
31
|
+
],
|
|
30
32
|
processEntities: true,
|
|
31
33
|
stopNodes: []
|
|
32
34
|
};
|
|
@@ -192,19 +194,26 @@ function buildEmptyObjNode(val, key, attrStr, level) {
|
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
function buildTextValNode(val, key, attrStr, level) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
197
|
+
const textValue = this.replaceEntitiesValue(val);
|
|
198
|
+
|
|
199
|
+
if( textValue === '' && this.options.unpairedTags.indexOf(key) !== -1){ //unpaired
|
|
200
|
+
if(this.options.suppressUnpairedNode){
|
|
201
|
+
return this.indentate(level) + '<' + key + this.tagEndChar;
|
|
202
|
+
}else{
|
|
203
|
+
return this.indentate(level) + '<' + key + "/" + this.tagEndChar;
|
|
204
|
+
}
|
|
205
|
+
}else{
|
|
206
|
+
return (
|
|
207
|
+
this.indentate(level) + '<' + key + attrStr + '>' +
|
|
208
|
+
textValue +
|
|
209
|
+
'</' + key + this.tagEndChar );
|
|
210
|
+
}
|
|
202
211
|
}
|
|
203
212
|
|
|
204
213
|
function replaceEntitiesValue(textValue){
|
|
205
214
|
if(textValue && textValue.length > 0 && this.options.processEntities){
|
|
206
|
-
for (
|
|
207
|
-
const entity = this.options.entities[
|
|
215
|
+
for (let i=0; i<this.options.entities.length; i++) {
|
|
216
|
+
const entity = this.options.entities[i];
|
|
208
217
|
textValue = textValue.replace(entity.regex, entity.val);
|
|
209
218
|
}
|
|
210
219
|
}
|
|
@@ -212,13 +221,17 @@ function replaceEntitiesValue(textValue){
|
|
|
212
221
|
}
|
|
213
222
|
|
|
214
223
|
function buildEmptyTextNode(val, key, attrStr, level) {
|
|
215
|
-
if( val === '' && this.options.unpairedTags.indexOf(key) !== -1){
|
|
216
|
-
|
|
217
|
-
|
|
224
|
+
if( val === '' && this.options.unpairedTags.indexOf(key) !== -1){ //unpaired
|
|
225
|
+
if(this.options.suppressUnpairedNode){
|
|
226
|
+
return this.indentate(level) + '<' + key + this.tagEndChar;
|
|
227
|
+
}else{
|
|
228
|
+
return this.indentate(level) + '<' + key + "/" + this.tagEndChar;
|
|
229
|
+
}
|
|
230
|
+
}else if (val !== '') { //empty
|
|
218
231
|
return this.buildTextValNode(val, key, attrStr, level);
|
|
219
232
|
} else {
|
|
220
|
-
if(key[0] === "?") return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
|
|
221
|
-
else return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
|
|
233
|
+
if(key[0] === "?") return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar; //PI tag
|
|
234
|
+
else return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar; //normal
|
|
222
235
|
}
|
|
223
236
|
}
|
|
224
237
|
|
|
@@ -48,7 +48,8 @@ function arrToStr(arr, options, jPath, level){
|
|
|
48
48
|
let tagStart = indentation + `<${tagName}${attStr}`;
|
|
49
49
|
let tagValue = arrToStr(tagObj[tagName], options, newJPath, level + 1);
|
|
50
50
|
if(options.unpairedTags.indexOf(tagName) !== -1){
|
|
51
|
-
xmlStr += tagStart + ">";
|
|
51
|
+
if(options.suppressUnpairedNode) xmlStr += tagStart + ">";
|
|
52
|
+
else xmlStr += tagStart + "/>";
|
|
52
53
|
}else if( (!tagValue || tagValue.length === 0) && options.suppressEmptyNode){
|
|
53
54
|
xmlStr += tagStart + "/>";
|
|
54
55
|
}else{
|
|
@@ -95,8 +96,8 @@ function isStopNode(jPath, options){
|
|
|
95
96
|
|
|
96
97
|
function replaceEntitiesValue(textValue, options){
|
|
97
98
|
if(textValue && textValue.length > 0 && options.processEntities){
|
|
98
|
-
for (
|
|
99
|
-
const entity = options.entities[
|
|
99
|
+
for (let i=0; i< options.entities.length; i++) {
|
|
100
|
+
const entity = options.entities[i];
|
|
100
101
|
textValue = textValue.replace(entity.regex, entity.val);
|
|
101
102
|
}
|
|
102
103
|
}
|
|
@@ -203,21 +203,29 @@ const parseXml = function(xmlData) {
|
|
|
203
203
|
textData = "";
|
|
204
204
|
i = closeIndex;
|
|
205
205
|
} else if( xmlData[i+1] === '?') {
|
|
206
|
+
|
|
206
207
|
let tagData = readTagExp(xmlData,i, false, "?>");
|
|
207
208
|
if(!tagData) throw new Error("Pi Tag is not closed.");
|
|
209
|
+
|
|
208
210
|
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
211
|
+
if( (this.options.ignoreDeclaration && tagData.tagName === "?xml") || this.options.ignorePiTags){
|
|
212
|
+
|
|
213
|
+
}else{
|
|
214
|
+
|
|
215
|
+
const childNode = new xmlNode(tagData.tagName);
|
|
216
|
+
childNode.add(this.options.textNodeName, "");
|
|
217
|
+
|
|
218
|
+
if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
|
|
219
|
+
childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath);
|
|
220
|
+
}
|
|
221
|
+
currentNode.addChild(childNode);
|
|
209
222
|
|
|
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
223
|
}
|
|
216
|
-
|
|
224
|
+
|
|
217
225
|
|
|
218
226
|
i = tagData.closeIndex + 1;
|
|
219
227
|
} else if(xmlData.substr(i + 1, 3) === '!--') {
|
|
220
|
-
const endIndex = findClosingIndex(xmlData, "-->", i, "Comment is not closed.")
|
|
228
|
+
const endIndex = findClosingIndex(xmlData, "-->", i+4, "Comment is not closed.")
|
|
221
229
|
if(this.options.commentPropName){
|
|
222
230
|
const comment = xmlData.substring(i + 4, endIndex - 2);
|
|
223
231
|
|