fast-xml-parser 4.1.4 → 4.2.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 CHANGED
@@ -1,9 +1,15 @@
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.1 / 2023-04-18**
4
+ * fix: jpath after unpaired tags
5
+
6
+ **4.2.0 / 2023-04-09**
7
+ * support `updateTag` parser property
8
+
3
9
  **4.1.4 / 2023-04-08**
4
10
  * update typings to let user create XMLBuilder instance without options (#556) (By [Patrick](https://github.com/omggga))
5
11
  * fix: IsArray option isn't parsing tags with 0 as value correctly #490 (#557) (By [Aleksandr Murashkin](https://github.com/p-kuen))
6
- * feature: support oneListGroup to group repeated children tags udder single group
12
+ * feature: support `oneListGroup` to group repeated children tags udder single group
7
13
 
8
14
  **4.1.3 / 2023-02-26**
9
15
  * fix #546: Support complex entity value
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-xml-parser",
3
- "version": "4.1.4",
3
+ "version": "4.2.1",
4
4
  "description": "Validate XML, Parse XML, Build XML without C/C++ based libraries",
5
5
  "main": "./src/fxp.js",
6
6
  "scripts": {
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;
@@ -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
 
@@ -205,9 +206,18 @@ const parseXml = function(xmlData) {
205
206
  textData = this.saveTextToParentTag(textData, currentNode, jPath);
206
207
  }
207
208
 
208
- jPath = jPath.substr(0, jPath.lastIndexOf("."));
209
-
210
- currentNode = this.tagsNodeStack.pop();//avoid recurssion, set the parent tag scope
209
+ //check if last tag of nested tag was unpaired tag
210
+ const lastTagName = jPath.substring(jPath.lastIndexOf(".")+1);
211
+ let propIndex = 0
212
+ if(lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1 ){
213
+ propIndex = jPath.lastIndexOf('.', jPath.lastIndexOf('.')-1)
214
+ if(propIndex < 1) propIndex = jPath.lastIndexOf(".");
215
+ }else{
216
+ propIndex = jPath.lastIndexOf(".");
217
+ }
218
+ jPath = jPath.substring(0, propIndex);
219
+
220
+ currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope
211
221
  textData = "";
212
222
  i = closeIndex;
213
223
  } else if( xmlData[i+1] === '?') {
@@ -224,9 +234,9 @@ const parseXml = function(xmlData) {
224
234
  childNode.add(this.options.textNodeName, "");
225
235
 
226
236
  if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
227
- childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath);
237
+ childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
228
238
  }
229
- currentNode.addChild(childNode);
239
+ this.addChild(currentNode, childNode, jPath)
230
240
 
231
241
  }
232
242
 
@@ -283,23 +293,22 @@ const parseXml = function(xmlData) {
283
293
  }
284
294
  }
285
295
 
286
- if(tagName !== xmlObj.tagname){
287
- jPath += jPath ? "." + tagName : tagName;
288
- }
289
-
290
296
  //check if last tag was unpaired tag
291
297
  const lastTag = currentNode;
292
298
  if(lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1 ){
293
299
  currentNode = this.tagsNodeStack.pop();
300
+ jPath = jPath.substring(0, jPath.lastIndexOf("."));
301
+ }
302
+ if(tagName !== xmlObj.tagname){
303
+ jPath += jPath ? "." + tagName : tagName;
294
304
  }
295
-
296
305
  if (this.isItStopNode(this.options.stopNodes, jPath, tagName)) { //TODO: namespace
297
306
  let tagContent = "";
298
307
  //self-closing tag
299
308
  if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
300
309
  i = result.closeIndex;
301
310
  }
302
- //boolean tag
311
+ //unpaired tag
303
312
  else if(this.options.unpairedTags.indexOf(tagName) !== -1){
304
313
  i = result.closeIndex;
305
314
  }
@@ -314,7 +323,7 @@ const parseXml = function(xmlData) {
314
323
 
315
324
  const childNode = new xmlNode(tagName);
316
325
  if(tagName !== tagExp && attrExpPresent){
317
- childNode[":@"] = this.buildAttributesMap(tagExp, jPath);
326
+ childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
318
327
  }
319
328
  if(tagContent) {
320
329
  tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
@@ -323,7 +332,7 @@ const parseXml = function(xmlData) {
323
332
  jPath = jPath.substr(0, jPath.lastIndexOf("."));
324
333
  childNode.add(this.options.textNodeName, tagContent);
325
334
 
326
- currentNode.addChild(childNode);
335
+ this.addChild(currentNode, childNode, jPath)
327
336
  }else{
328
337
  //selfClosing tag
329
338
  if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
@@ -340,10 +349,10 @@ const parseXml = function(xmlData) {
340
349
 
341
350
  const childNode = new xmlNode(tagName);
342
351
  if(tagName !== tagExp && attrExpPresent){
343
- childNode[":@"] = this.buildAttributesMap(tagExp, jPath);
352
+ childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
344
353
  }
354
+ this.addChild(currentNode, childNode, jPath)
345
355
  jPath = jPath.substr(0, jPath.lastIndexOf("."));
346
- currentNode.addChild(childNode);
347
356
  }
348
357
  //opening tag
349
358
  else{
@@ -351,9 +360,9 @@ const parseXml = function(xmlData) {
351
360
  this.tagsNodeStack.push(currentNode);
352
361
 
353
362
  if(tagName !== tagExp && attrExpPresent){
354
- childNode[":@"] = this.buildAttributesMap(tagExp, jPath);
363
+ childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
355
364
  }
356
- currentNode.addChild(childNode);
365
+ this.addChild(currentNode, childNode, jPath)
357
366
  currentNode = childNode;
358
367
  }
359
368
  textData = "";
@@ -367,6 +376,17 @@ const parseXml = function(xmlData) {
367
376
  return xmlObj.child;
368
377
  }
369
378
 
379
+ function addChild(currentNode, childNode, jPath){
380
+ const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"])
381
+ if(result === false){
382
+ }else if(typeof result === "string"){
383
+ childNode.tagname = result
384
+ currentNode.addChild(childNode);
385
+ }else{
386
+ currentNode.addChild(childNode);
387
+ }
388
+ }
389
+
370
390
  const replaceEntitiesValue = function(val){
371
391
 
372
392
  if(this.options.processEntities){
@@ -423,7 +443,7 @@ function isItStopNode(stopNodes, jPath, currentTagName){
423
443
  }
424
444
 
425
445
  /**
426
- * Returns the tag Expression and where it is ending handling single-dobule quotes situation
446
+ * Returns the tag Expression and where it is ending handling single-double quotes situation
427
447
  * @param {string} xmlData
428
448
  * @param {number} i starting index
429
449
  * @returns