fast-xml-parser 4.1.3 → 4.2.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 +8 -0
- package/README.md +1 -0
- package/package.json +1 -1
- package/src/fxp.d.ts +10 -1
- package/src/xmlbuilder/json2xml.js +12 -2
- package/src/xmlparser/OptionsBuilder.js +4 -0
- package/src/xmlparser/OrderedObjParser.js +24 -12
- package/src/xmlparser/node2json.js +13 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
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.2.0 / 2023-04-09**
|
|
4
|
+
* support `updateTag` parser property
|
|
5
|
+
|
|
6
|
+
**4.1.4 / 2023-04-08**
|
|
7
|
+
* update typings to let user create XMLBuilder instance without options (#556) (By [Patrick](https://github.com/omggga))
|
|
8
|
+
* fix: IsArray option isn't parsing tags with 0 as value correctly #490 (#557) (By [Aleksandr Murashkin](https://github.com/p-kuen))
|
|
9
|
+
* feature: support `oneListGroup` to group repeated children tags udder single group
|
|
10
|
+
|
|
3
11
|
**4.1.3 / 2023-02-26**
|
|
4
12
|
* fix #546: Support complex entity value
|
|
5
13
|
|
package/README.md
CHANGED
|
@@ -54,6 +54,7 @@ Check [ThankYouBackers](https://github.com/NaturalIntelligence/ThankYouBackers)
|
|
|
54
54
|
<a href="http://www.magento.com/" title="Magento" > <img src="https://avatars2.githubusercontent.com/u/168457" width="60px" ></a>
|
|
55
55
|
<a href="https://github.com/SAP" title="SAP" > <img src="https://user-images.githubusercontent.com/7692328/204835214-d9d25b58-e3df-408d-87a3-c7d36b578ee4.png" width="60px" ></a>
|
|
56
56
|
<a href="https://github.com/postmanlabs" title="postman" > <img src="https://user-images.githubusercontent.com/7692328/204835529-e9e290ad-696a-49ad-9d34-08e955704715.png" width="60px" ></a>
|
|
57
|
+
<a href="https://github.com/react-native-community" title="React Native Community" > <img src="https://avatars.githubusercontent.com/u/20269980?v=4" width="60px" ></a>
|
|
57
58
|
|
|
58
59
|
Check the list of all known users [here](./USERs.md);
|
|
59
60
|
|
package/package.json
CHANGED
package/src/fxp.d.ts
CHANGED
|
@@ -32,6 +32,14 @@ Control how tag value should be parsed. Called only if tag value is not empty
|
|
|
32
32
|
ignorePiTags: boolean;
|
|
33
33
|
transformTagName: ((tagName: string) => string) | false;
|
|
34
34
|
transformAttributeName: ((attributeName: string) => string) | false;
|
|
35
|
+
/**
|
|
36
|
+
Change the tag name when a different name is returned. Skip the tag from parsed result when false is returned.
|
|
37
|
+
Modify `attrs` object to control attributes for the given tag.
|
|
38
|
+
|
|
39
|
+
@returns {string} new tag name.
|
|
40
|
+
@returns false to skip the tag
|
|
41
|
+
*/
|
|
42
|
+
updateTag: (tagName: string, jPath: string, attrs: {[k: string]: string}) => string | boolean;
|
|
35
43
|
};
|
|
36
44
|
type strnumOptions = {
|
|
37
45
|
hex: boolean;
|
|
@@ -65,6 +73,7 @@ type XmlBuilderOptions = {
|
|
|
65
73
|
tagValueProcessor: (name: string, value: unknown) => string;
|
|
66
74
|
attributeValueProcessor: (name: string, value: unknown) => string;
|
|
67
75
|
processEntities: boolean;
|
|
76
|
+
oneListGroup: boolean;
|
|
68
77
|
};
|
|
69
78
|
type XmlBuilderOptionsOptional = Partial<XmlBuilderOptions>;
|
|
70
79
|
|
|
@@ -94,6 +103,6 @@ export class XMLValidator{
|
|
|
94
103
|
static validate( xmlData: string, options?: validationOptionsOptional): true | ValidationError;
|
|
95
104
|
}
|
|
96
105
|
export class XMLBuilder {
|
|
97
|
-
constructor(options
|
|
106
|
+
constructor(options?: XmlBuilderOptionsOptional);
|
|
98
107
|
build(jObj: any): any;
|
|
99
108
|
}
|
|
@@ -33,6 +33,7 @@ const defaultOptions = {
|
|
|
33
33
|
stopNodes: [],
|
|
34
34
|
// transformTagName: false,
|
|
35
35
|
// transformAttributeName: false,
|
|
36
|
+
oneListGroup: false
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
function Builder(options) {
|
|
@@ -103,6 +104,7 @@ Builder.prototype.j2x = function(jObj, level) {
|
|
|
103
104
|
} else if (Array.isArray(jObj[key])) {
|
|
104
105
|
//repeated nodes
|
|
105
106
|
const arrLen = jObj[key].length;
|
|
107
|
+
let listTagVal = "";
|
|
106
108
|
for (let j = 0; j < arrLen; j++) {
|
|
107
109
|
const item = jObj[key][j];
|
|
108
110
|
if (typeof item === 'undefined') {
|
|
@@ -112,11 +114,19 @@ Builder.prototype.j2x = function(jObj, level) {
|
|
|
112
114
|
else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
113
115
|
// val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
|
|
114
116
|
} else if (typeof item === 'object') {
|
|
115
|
-
|
|
117
|
+
if(this.options.oneListGroup ){
|
|
118
|
+
listTagVal += this.j2x(item, level + 1).val;
|
|
119
|
+
}else{
|
|
120
|
+
listTagVal += this.processTextOrObjNode(item, key, level)
|
|
121
|
+
}
|
|
116
122
|
} else {
|
|
117
|
-
|
|
123
|
+
listTagVal += this.buildTextValNode(item, key, '', level);
|
|
118
124
|
}
|
|
119
125
|
}
|
|
126
|
+
if(this.options.oneListGroup){
|
|
127
|
+
listTagVal = this.buildObjectNode(listTagVal, key, '', level);
|
|
128
|
+
}
|
|
129
|
+
val += listTagVal;
|
|
120
130
|
} else {
|
|
121
131
|
//nested node
|
|
122
132
|
if (this.options.attributesGroupName && key === this.options.attributesGroupName) {
|
|
@@ -34,6 +34,10 @@ const defaultOptions = {
|
|
|
34
34
|
ignorePiTags: false,
|
|
35
35
|
transformTagName: false,
|
|
36
36
|
transformAttributeName: false,
|
|
37
|
+
updateTag: function(tagName, jPath, attrs){
|
|
38
|
+
return tagName
|
|
39
|
+
},
|
|
40
|
+
// skipEmptyListItem: false
|
|
37
41
|
};
|
|
38
42
|
|
|
39
43
|
const buildOptions = function(options) {
|
|
@@ -50,6 +50,7 @@ class OrderedObjParser{
|
|
|
50
50
|
this.replaceEntitiesValue = replaceEntitiesValue;
|
|
51
51
|
this.readStopNodeData = readStopNodeData;
|
|
52
52
|
this.saveTextToParentTag = saveTextToParentTag;
|
|
53
|
+
this.addChild = addChild;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
}
|
|
@@ -121,7 +122,7 @@ function resolveNameSpace(tagname) {
|
|
|
121
122
|
//const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
|
|
122
123
|
const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
|
|
123
124
|
|
|
124
|
-
function buildAttributesMap(attrStr, jPath) {
|
|
125
|
+
function buildAttributesMap(attrStr, jPath, tagName) {
|
|
125
126
|
if (!this.options.ignoreAttributes && typeof attrStr === 'string') {
|
|
126
127
|
// attrStr = attrStr.replace(/\r?\n/g, ' ');
|
|
127
128
|
//attrStr = attrStr || attrStr.trim();
|
|
@@ -171,7 +172,7 @@ function buildAttributesMap(attrStr, jPath) {
|
|
|
171
172
|
attrCollection[this.options.attributesGroupName] = attrs;
|
|
172
173
|
return attrCollection;
|
|
173
174
|
}
|
|
174
|
-
return attrs
|
|
175
|
+
return attrs
|
|
175
176
|
}
|
|
176
177
|
}
|
|
177
178
|
|
|
@@ -207,7 +208,7 @@ const parseXml = function(xmlData) {
|
|
|
207
208
|
|
|
208
209
|
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
209
210
|
|
|
210
|
-
currentNode = this.tagsNodeStack.pop();//avoid
|
|
211
|
+
currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope
|
|
211
212
|
textData = "";
|
|
212
213
|
i = closeIndex;
|
|
213
214
|
} else if( xmlData[i+1] === '?') {
|
|
@@ -224,9 +225,9 @@ const parseXml = function(xmlData) {
|
|
|
224
225
|
childNode.add(this.options.textNodeName, "");
|
|
225
226
|
|
|
226
227
|
if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
|
|
227
|
-
childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath);
|
|
228
|
+
childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
|
|
228
229
|
}
|
|
229
|
-
|
|
230
|
+
this.addChild(currentNode, childNode, jPath)
|
|
230
231
|
|
|
231
232
|
}
|
|
232
233
|
|
|
@@ -314,7 +315,7 @@ const parseXml = function(xmlData) {
|
|
|
314
315
|
|
|
315
316
|
const childNode = new xmlNode(tagName);
|
|
316
317
|
if(tagName !== tagExp && attrExpPresent){
|
|
317
|
-
childNode[":@"] = this.buildAttributesMap(tagExp, jPath);
|
|
318
|
+
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
318
319
|
}
|
|
319
320
|
if(tagContent) {
|
|
320
321
|
tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
|
|
@@ -323,7 +324,7 @@ const parseXml = function(xmlData) {
|
|
|
323
324
|
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
324
325
|
childNode.add(this.options.textNodeName, tagContent);
|
|
325
326
|
|
|
326
|
-
|
|
327
|
+
this.addChild(currentNode, childNode, jPath)
|
|
327
328
|
}else{
|
|
328
329
|
//selfClosing tag
|
|
329
330
|
if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
|
|
@@ -340,10 +341,10 @@ const parseXml = function(xmlData) {
|
|
|
340
341
|
|
|
341
342
|
const childNode = new xmlNode(tagName);
|
|
342
343
|
if(tagName !== tagExp && attrExpPresent){
|
|
343
|
-
childNode[":@"] = this.buildAttributesMap(tagExp, jPath);
|
|
344
|
+
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
344
345
|
}
|
|
345
346
|
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
346
|
-
|
|
347
|
+
this.addChild(currentNode, childNode, jPath)
|
|
347
348
|
}
|
|
348
349
|
//opening tag
|
|
349
350
|
else{
|
|
@@ -351,9 +352,9 @@ const parseXml = function(xmlData) {
|
|
|
351
352
|
this.tagsNodeStack.push(currentNode);
|
|
352
353
|
|
|
353
354
|
if(tagName !== tagExp && attrExpPresent){
|
|
354
|
-
childNode[":@"] = this.buildAttributesMap(tagExp, jPath);
|
|
355
|
+
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
355
356
|
}
|
|
356
|
-
|
|
357
|
+
this.addChild(currentNode, childNode, jPath)
|
|
357
358
|
currentNode = childNode;
|
|
358
359
|
}
|
|
359
360
|
textData = "";
|
|
@@ -367,6 +368,17 @@ const parseXml = function(xmlData) {
|
|
|
367
368
|
return xmlObj.child;
|
|
368
369
|
}
|
|
369
370
|
|
|
371
|
+
function addChild(currentNode, childNode, jPath){
|
|
372
|
+
const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"])
|
|
373
|
+
if(result === false){
|
|
374
|
+
}else if(typeof result === "string"){
|
|
375
|
+
childNode.tagname = result
|
|
376
|
+
currentNode.addChild(childNode);
|
|
377
|
+
}else{
|
|
378
|
+
currentNode.addChild(childNode);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
370
382
|
const replaceEntitiesValue = function(val){
|
|
371
383
|
|
|
372
384
|
if(this.options.processEntities){
|
|
@@ -423,7 +435,7 @@ function isItStopNode(stopNodes, jPath, currentTagName){
|
|
|
423
435
|
}
|
|
424
436
|
|
|
425
437
|
/**
|
|
426
|
-
* Returns the tag Expression and where it is ending handling single-
|
|
438
|
+
* Returns the tag Expression and where it is ending handling single-double quotes situation
|
|
427
439
|
* @param {string} xmlData
|
|
428
440
|
* @param {number} i starting index
|
|
429
441
|
* @returns
|
|
@@ -94,8 +94,20 @@ function assignAttributes(obj, attrMap, jpath, options){
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
function isLeafTag(obj, options){
|
|
97
|
+
const { textNodeName } = options;
|
|
97
98
|
const propCount = Object.keys(obj).length;
|
|
98
|
-
|
|
99
|
+
|
|
100
|
+
if (propCount === 0) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (
|
|
105
|
+
propCount === 1 &&
|
|
106
|
+
(obj[textNodeName] || typeof obj[textNodeName] === "boolean" || obj[textNodeName] === 0)
|
|
107
|
+
) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
99
111
|
return false;
|
|
100
112
|
}
|
|
101
113
|
exports.prettify = prettify;
|