fast-xml-parser 4.0.6 → 4.0.9

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,21 @@
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.9 / 2022-07-10**
4
+ * fix #470: stop-tag can have self-closing tag with same name
5
+ * fix #472: stopNode can have any special tag inside
6
+ * Allow !ATTLIST and !NOTATION with DOCTYPE
7
+ * Add transformTagName option to transform tag names when parsing (#469) (By [Erik Rothoff Andersson](https://github.com/erkie))
8
+
9
+ **4.0.8 / 2022-05-28**
10
+ * Fix CDATA parsing returning empty string when value = 0 (#451) (By [ndelanou](https://github.com/ndelanou))
11
+ * Fix stopNodes when same tag appears inside node (#456) (By [patrickshipe](https://github.com/patrickshipe))
12
+ * fix #468: prettify own properties only
13
+
14
+ **4.0.7 / 2022-03-18**
15
+ * support CDATA even if tag order is not preserved
16
+ * support Comments even if tag order is not preserved
17
+ * fix #446: XMLbuilder should not indent XML declaration
18
+
3
19
  **4.0.6 / 2022-03-08**
4
20
  * fix: call tagValueProcessor only once for array items
5
21
  * fix: missing changed for #437
package/README.md CHANGED
@@ -11,6 +11,8 @@
11
11
 
12
12
  Validate XML, Parse XML to JS Object, or Build XML from JS Object without C/C++ based libraries and no callback.
13
13
 
14
+ > Looking for maintainers
15
+
14
16
  <a href="https://opencollective.com/fast-xml-parser/donate" target="_blank">
15
17
  <img src="https://opencollective.com/fast-xml-parser/donate/button@2x.png?color=blue" width=200 />
16
18
  </a>
@@ -18,6 +20,8 @@ Validate XML, Parse XML to JS Object, or Build XML from JS Object without C/C++
18
20
 
19
21
  Check [ThankYouBackers](https://github.com/NaturalIntelligence/ThankYouBackers) for our contributors
20
22
 
23
+
24
+
21
25
  [![](static/img/ni_ads_ads.gif)](https://github.com/NaturalIntelligence/ads/)
22
26
  ## Users
23
27
 
@@ -100,12 +104,12 @@ In a HTML page
100
104
 
101
105
  Check lib folder for different browser bundles
102
106
 
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 |
107
+ | Bundle Name | Size |
108
+ | ------------------ | ---- |
109
+ | fxbuilder.min.js | 5.2K |
110
+ | fxparser.js | 50K |
111
+ | fxparser.min.js | 17K |
112
+ | fxp.min.js | 22K |
109
113
  | fxvalidator.min.js | 5.7K |
110
114
 
111
115
  ### Documents
@@ -140,7 +144,7 @@ Check lib folder for different browser bundles
140
144
  * **[BigBit standard](https://github.com/amitguptagwl/bigbit)** :
141
145
  * Single text encoding to replace UTF-8, UTF-16, UTF-32 and more with less memory.
142
146
  * Single Numeric datatype alternative of integer, float, double, long, decimal and more without precision loss.
143
- * **[Cytorus](https://github.com/NaturalIntelligence/cytorus)**: Now be specific and flexible while running E2E tests.
147
+ * **[Cytorus](https://github.com/NaturalIntelligence/cytorus)**: Be specific and flexible while running E2E tests.
144
148
  * Run tests only for a particular User Story
145
149
  * Run tests for a route or from a route
146
150
  * Customizable reporting
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-xml-parser",
3
- "version": "4.0.6",
3
+ "version": "4.0.9",
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
@@ -22,6 +22,7 @@ type X2jOptions = {
22
22
  htmlEntities: boolean;
23
23
  ignoreDeclaration: boolean;
24
24
  ignorePiTags: boolean;
25
+ transformTagName: ((tagName: string) => string) | false;
25
26
  };
26
27
  type strnumOptions = {
27
28
  hex: boolean;
@@ -30,7 +30,8 @@ const defaultOptions = {
30
30
  { regex: new RegExp("\"", "g"), val: "&quot;" }
31
31
  ],
32
32
  processEntities: true,
33
- stopNodes: []
33
+ stopNodes: [],
34
+ transformTagName: false,
34
35
  };
35
36
 
36
37
  function Builder(options) {
@@ -172,11 +173,10 @@ function buildObjectNode(val, key, attrStr, level) {
172
173
  }
173
174
 
174
175
  if (attrStr && val.indexOf('<') === -1) {
175
- return (
176
- this.indentate(level) + '<' + key + attrStr + piClosingChar + '>' +
177
- val +
178
- tagEndExp );
179
- } else {
176
+ return ( this.indentate(level) + '<' + key + attrStr + piClosingChar + '>' + val + tagEndExp );
177
+ } else if (this.options.commentPropName !== false && key === this.options.commentPropName && piClosingChar.length === 0) {
178
+ return this.indentate(level) + `<!--${val}-->` + this.newLine;
179
+ }else {
180
180
  return (
181
181
  this.indentate(level) + '<' + key + attrStr + piClosingChar + this.tagEndChar +
182
182
  val +
@@ -194,20 +194,27 @@ function buildEmptyObjNode(val, key, attrStr, level) {
194
194
  }
195
195
 
196
196
  function buildTextValNode(val, key, attrStr, level) {
197
- let textValue = this.options.tagValueProcessor(key, val);
198
- textValue = this.replaceEntitiesValue(textValue);
199
-
200
- if( textValue === '' && this.options.unpairedTags.indexOf(key) !== -1){ //unpaired
201
- if(this.options.suppressUnpairedNode){
202
- return this.indentate(level) + '<' + key + this.tagEndChar;
203
- }else{
204
- return this.indentate(level) + '<' + key + "/" + this.tagEndChar;
205
- }
197
+ if (this.options.cdataPropName !== false && key === this.options.cdataPropName) {
198
+ return this.indentate(level) + `<![CDATA[${val}]]>` + this.newLine;
199
+ }else if (this.options.commentPropName !== false && key === this.options.commentPropName) {
200
+ return this.indentate(level) + `<!--${val}-->` + this.newLine;
206
201
  }else{
207
- return (
208
- this.indentate(level) + '<' + key + attrStr + '>' +
209
- textValue +
210
- '</' + key + this.tagEndChar );
202
+ let textValue = this.options.tagValueProcessor(key, val);
203
+ textValue = this.replaceEntitiesValue(textValue);
204
+
205
+ if( textValue === '' && this.options.unpairedTags.indexOf(key) !== -1){ //unpaired
206
+ if(this.options.suppressUnpairedNode){
207
+ return this.indentate(level) + '<' + key + this.tagEndChar;
208
+ }else{
209
+ return this.indentate(level) + '<' + key + "/" + this.tagEndChar;
210
+ }
211
+ } else{
212
+ return (
213
+ this.indentate(level) + '<' + key + attrStr + '>' +
214
+ textValue +
215
+ '</' + key + this.tagEndChar );
216
+ }
217
+
211
218
  }
212
219
  }
213
220
 
@@ -41,7 +41,10 @@ function arrToStr(arr, options, jPath, level){
41
41
  continue;
42
42
  }else if( tagName[0] === "?"){
43
43
  const attStr = attr_to_str(tagObj[":@"], options);
44
- xmlStr += indentation + `<${tagName} ${tagObj[tagName][0][options.textNodeName]} ${attStr}?>`;
44
+ const tempInd = tagName === "?xml" ? "" : indentation;
45
+ let piTextNodeName = tagObj[tagName][0][options.textNodeName];
46
+ piTextNodeName = piTextNodeName.length !== 0 ? " " + piTextNodeName : ""; //remove extra spacing
47
+ xmlStr += tempInd + `<${tagName}${piTextNodeName}${attStr}?>`;
45
48
  continue;
46
49
  }
47
50
  const attStr = attr_to_str(tagObj[":@"], options);
@@ -38,6 +38,31 @@ function readDocType(xmlData, i){
38
38
  ){
39
39
  //Not supported
40
40
  i += 8;
41
+ }else if( hasBody &&
42
+ xmlData[i+1] === '!' &&
43
+ xmlData[i+2] === 'A' &&
44
+ xmlData[i+3] === 'T' &&
45
+ xmlData[i+4] === 'T' &&
46
+ xmlData[i+5] === 'L' &&
47
+ xmlData[i+6] === 'I' &&
48
+ xmlData[i+7] === 'S' &&
49
+ xmlData[i+8] === 'T'
50
+ ){
51
+ //Not supported
52
+ i += 8;
53
+ }else if( hasBody &&
54
+ xmlData[i+1] === '!' &&
55
+ xmlData[i+2] === 'N' &&
56
+ xmlData[i+3] === 'O' &&
57
+ xmlData[i+4] === 'T' &&
58
+ xmlData[i+5] === 'A' &&
59
+ xmlData[i+6] === 'T' &&
60
+ xmlData[i+7] === 'I' &&
61
+ xmlData[i+8] === 'O' &&
62
+ xmlData[i+9] === 'N'
63
+ ){
64
+ //Not supported
65
+ i += 9;
41
66
  }else if( //comment
42
67
  xmlData[i+1] === '!' &&
43
68
  xmlData[i+2] === '-' &&
@@ -30,7 +30,8 @@ const defaultOptions = {
30
30
  processEntities: true,
31
31
  htmlEntities: false,
32
32
  ignoreDeclaration: false,
33
- ignorePiTags: false
33
+ ignorePiTags: false,
34
+ transformTagName: false,
34
35
  };
35
36
 
36
37
  const buildOptions = function(options) {
@@ -193,6 +193,10 @@ const parseXml = function(xmlData) {
193
193
  }
194
194
  }
195
195
 
196
+ if(this.options.transformTagName) {
197
+ tagName = this.options.transformTagName(tagName);
198
+ }
199
+
196
200
  if(currentNode){
197
201
  textData = this.saveTextToParentTag(textData, currentNode, jPath);
198
202
  }
@@ -251,18 +255,21 @@ const parseXml = function(xmlData) {
251
255
  currentNode.add(this.options.cdataPropName, [ { [this.options.textNodeName] : tagExp } ]);
252
256
  }else{
253
257
  let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true);
254
- if(!val) val = "";
258
+ if(val == undefined) val = "";
255
259
  currentNode.add(this.options.textNodeName, val);
256
260
  }
257
261
 
258
262
  i = closeIndex + 2;
259
263
  }else {//Opening tag
260
-
261
264
  let result = readTagExp(xmlData,i, this. options.removeNSPrefix);
262
265
  let tagName= result.tagName;
263
266
  let tagExp = result.tagExp;
264
267
  let attrExpPresent = result.attrExpPresent;
265
268
  let closeIndex = result.closeIndex;
269
+
270
+ if (this.options.transformTagName) {
271
+ tagName = this.options.transformTagName(tagName);
272
+ }
266
273
 
267
274
  //save text as child node
268
275
  if (currentNode && textData) {
@@ -322,6 +329,10 @@ const parseXml = function(xmlData) {
322
329
  }else{
323
330
  tagExp = tagExp.substr(0, tagExp.length - 1);
324
331
  }
332
+
333
+ if(this.options.transformTagName) {
334
+ tagName = this.options.transformTagName(tagName);
335
+ }
325
336
 
326
337
  const childNode = new xmlNode(tagName);
327
338
  if(tagName !== tagExp && attrExpPresent){
@@ -486,17 +497,44 @@ function readTagExp(xmlData,i, removeNSPrefix, closingChar = ">"){
486
497
  */
487
498
  function readStopNodeData(xmlData, tagName, i){
488
499
  const startIndex = i;
500
+ // Starting at 1 since we already have an open tag
501
+ let openTagCount = 1;
502
+
489
503
  for (; i < xmlData.length; i++) {
490
- if( xmlData[i] === "<" && xmlData[i+1] === "/"){
491
- const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
492
- let closeTagName = xmlData.substring(i+2,closeIndex).trim();
493
- if(closeTagName === tagName){
494
- return {
495
- tagContent: xmlData.substring(startIndex, i),
496
- i : closeIndex
504
+ if( xmlData[i] === "<"){
505
+ if (xmlData[i+1] === "/") {//close tag
506
+ const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
507
+ let closeTagName = xmlData.substring(i+2,closeIndex).trim();
508
+ if(closeTagName === tagName){
509
+ openTagCount--;
510
+ if (openTagCount === 0) {
511
+ return {
512
+ tagContent: xmlData.substring(startIndex, i),
513
+ i : closeIndex
514
+ }
515
+ }
516
+ }
517
+ i=closeIndex;
518
+ } else if(xmlData[i+1] === '?') {
519
+ const closeIndex = findClosingIndex(xmlData, "?>", i+1, "StopNode is not closed.")
520
+ i=closeIndex;
521
+ } else if(xmlData.substr(i + 1, 3) === '!--') {
522
+ const closeIndex = findClosingIndex(xmlData, "-->", i+3, "StopNode is not closed.")
523
+ i=closeIndex;
524
+ } else if(xmlData.substr(i + 1, 2) === '![') {
525
+ const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
526
+ i=closeIndex;
527
+ } else {
528
+ const tagData = readTagExp(xmlData, i, '>')
529
+
530
+ if (tagData) {
531
+ const openTagName = tagData && tagData.tagName;
532
+ if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length-1] !== "/") {
533
+ openTagCount++;
534
+ }
535
+ i=tagData.closeIndex;
497
536
  }
498
537
  }
499
- i=closeIndex;
500
538
  }
501
539
  }//end for loop
502
540
  }
@@ -46,9 +46,9 @@ function compress(arr, options, jPath){
46
46
  else val = "";
47
47
  }
48
48
 
49
- if(compressedObj[property] !== undefined) {
49
+ if(compressedObj[property] !== undefined && compressedObj.hasOwnProperty(property)) {
50
50
  if(!Array.isArray(compressedObj[property])) {
51
- compressedObj[property] = [ compressedObj[property] ];
51
+ compressedObj[property] = [ compressedObj[property] ];
52
52
  }
53
53
  compressedObj[property].push(val);
54
54
  }else{