fast-xml-parser 4.0.12 → 4.0.13

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,9 @@
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.13 / 2023-01-07**
4
+ * preserveorder formatting (By [mdeknowis](https://github.com/mdeknowis))
5
+ * support `transformAttributeName` (By [Erik Rothoff Andersson](https://github.com/erkie))
6
+
3
7
  **4.0.12 / 2022-11-19**
4
8
  * fix typescript
5
9
 
package/README.md CHANGED
@@ -13,16 +13,32 @@ Validate XML, Parse XML to JS Object, or Build XML from JS Object without C/C++
13
13
 
14
14
  > Looking for maintainers
15
15
 
16
+ Support this project by becoming a **Sponsor**.
17
+
18
+ Click on Sponsor button above or
16
19
  <a href="https://opencollective.com/fast-xml-parser/donate" target="_blank">
17
20
  <img src="https://opencollective.com/fast-xml-parser/donate/button@2x.png?color=blue" width=200 />
18
21
  </a>
19
22
  <a href="https://paypal.me/naturalintelligence"> <img src="static/img/support_paypal.svg" alt="Stubmatic donate button" width="200"/></a>
20
23
 
21
- Check [ThankYouBackers](https://github.com/NaturalIntelligence/ThankYouBackers) for our contributors
24
+
25
+ ### Sponsors
26
+
27
+ <a href="https://github.com/getsentry" target="_blank"><img src="https://user-images.githubusercontent.com/7692328/204701653-ce369b29-6fdb-48f2-9848-593d04628a2b.png" width="60px"></a>
28
+ <a href="https://opencollective.com/fast-xml-parser/sponsor/0/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/0/avatar.svg"></a>
29
+ <a href="https://opencollective.com/fast-xml-parser/sponsor/1/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/1/avatar.svg"></a>
30
+ <a href="https://opencollective.com/fast-xml-parser/sponsor/2/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/2/avatar.svg"></a>
31
+ <a href="https://opencollective.com/fast-xml-parser/sponsor/3/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/3/avatar.svg"></a>
32
+ <a href="https://opencollective.com/fast-xml-parser/sponsor/4/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/4/avatar.svg"></a>
33
+ <a href="https://opencollective.com/fast-xml-parser/sponsor/5/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/5/avatar.svg"></a>
34
+ <a href="https://opencollective.com/fast-xml-parser/sponsor/6/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/6/avatar.svg"></a>
35
+ <a href="https://opencollective.com/fast-xml-parser/sponsor/7/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/7/avatar.svg"></a>
36
+ <a href="https://opencollective.com/fast-xml-parser/sponsor/8/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/8/avatar.svg"></a>
37
+ <a href="https://opencollective.com/fast-xml-parser/sponsor/9/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/9/avatar.svg"></a>
22
38
 
23
39
 
40
+ Check [ThankYouBackers](https://github.com/NaturalIntelligence/ThankYouBackers) for our supporters
24
41
 
25
- [![](static/img/ni_ads_ads.gif)](https://github.com/NaturalIntelligence/ads/)
26
42
  ## Users
27
43
 
28
44
  <a href="https://github.com/renovatebot/renovate" title="renovate" ><img src="https://avatars1.githubusercontent.com/u/38656520" width="60px" ></a>
@@ -36,12 +52,14 @@ Check [ThankYouBackers](https://github.com/NaturalIntelligence/ThankYouBackers)
36
52
  <a href="https://github.com/aws" title="AWS SDK" > <img src="https://avatars.githubusercontent.com/u/2232217" width="60px" ></a>
37
53
  <a href="http://www.fda.gov/" title="Food and Drug Administration " > <img src="https://avatars2.githubusercontent.com/u/6471964" width="60px" ></a>
38
54
  <a href="http://www.magento.com/" title="Magento" > <img src="https://avatars2.githubusercontent.com/u/168457" width="60px" ></a>
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
+ <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>
39
57
 
40
58
  Check the list of all known users [here](./USERs.md);
41
59
 
42
60
  <small>The list of users is collected either from the list published by Github, cummunicated directly through mails/chat , or from other resources. If you feel that your name in the above list is incorrectly published or you're not the user of this library anymore then you can inform us to remove it. We'll do the necessary changes ASAP.</small>
43
61
 
44
- ### Main Features
62
+ ## Main Features
45
63
 
46
64
  <img align="right" src="static/img/fxp_logo.png" width="180px" alt="FXP logo"/>
47
65
 
@@ -82,7 +100,7 @@ $ fxparser some.xml
82
100
 
83
101
  In a node js project
84
102
  ```js
85
- const { XMLParser, XMLBuilder, XMLValidator} = require("../src/fxp");
103
+ const { XMLParser, XMLBuilder, XMLValidator} = require("fast-xml-parser");
86
104
 
87
105
  const parser = new XMLParser();
88
106
  let jObj = parser.parse(XMLdata);
@@ -130,6 +148,7 @@ Check lib folder for different browser bundles
130
148
  ![](./docs/imgs/XMLParser_v4.png)
131
149
  * Y-axis: requests per second
132
150
  * X-axis: File size
151
+
133
152
  **Large files**
134
153
  ![](./docs/imgs/XMLParser_large_v4.png)
135
154
  * Y-axis: requests per second
@@ -141,6 +160,8 @@ Check lib folder for different browser bundles
141
160
 
142
161
  <small>negative means error</small>
143
162
 
163
+ [![](static/img/ni_ads_ads.gif)](https://github.com/NaturalIntelligence/ads/)
164
+
144
165
  ## Our other projects and research you must try
145
166
 
146
167
  * **[BigBit standard](https://github.com/amitguptagwl/bigbit)** :
@@ -175,20 +196,6 @@ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com
175
196
  <a href="https://opencollective.com/fast-xml-parser#backers" target="_blank"><img src="https://opencollective.com/fast-xml-parser/backers.svg?width=890"></a>
176
197
 
177
198
 
178
- ### Sponsors
179
-
180
- <small>[[Become a sponsor](https://opencollective.com/fast-xml-parser#sponsor)] Support this project by becoming a sponsor. Your logo will show up here with a link to your website. Please also share your detail so we can thankyou on SocialMedia.</small>
181
-
182
- <a href="https://opencollective.com/fast-xml-parser/sponsor/0/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/0/avatar.svg"></a>
183
- <a href="https://opencollective.com/fast-xml-parser/sponsor/1/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/1/avatar.svg"></a>
184
- <a href="https://opencollective.com/fast-xml-parser/sponsor/2/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/2/avatar.svg"></a>
185
- <a href="https://opencollective.com/fast-xml-parser/sponsor/3/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/3/avatar.svg"></a>
186
- <a href="https://opencollective.com/fast-xml-parser/sponsor/4/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/4/avatar.svg"></a>
187
- <a href="https://opencollective.com/fast-xml-parser/sponsor/5/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/5/avatar.svg"></a>
188
- <a href="https://opencollective.com/fast-xml-parser/sponsor/6/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/6/avatar.svg"></a>
189
- <a href="https://opencollective.com/fast-xml-parser/sponsor/7/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/7/avatar.svg"></a>
190
- <a href="https://opencollective.com/fast-xml-parser/sponsor/8/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/8/avatar.svg"></a>
191
- <a href="https://opencollective.com/fast-xml-parser/sponsor/9/website" target="_blank"><img src="https://opencollective.com/fast-xml-parser/sponsor/9/avatar.svg"></a>
192
199
 
193
200
  # License
194
201
  * MIT License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-xml-parser",
3
- "version": "4.0.12",
3
+ "version": "4.0.13",
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
@@ -31,6 +31,7 @@ Control how tag value should be parsed. Called only if tag value is not empty
31
31
  ignoreDeclaration: boolean;
32
32
  ignorePiTags: boolean;
33
33
  transformTagName: ((tagName: string) => string) | false;
34
+ transformAttributeName: ((attributeName: string) => string) | false;
34
35
  };
35
36
  type strnumOptions = {
36
37
  hex: boolean;
@@ -31,7 +31,8 @@ const defaultOptions = {
31
31
  ],
32
32
  processEntities: true,
33
33
  stopNodes: [],
34
- transformTagName: false,
34
+ // transformTagName: false,
35
+ // transformAttributeName: false,
35
36
  };
36
37
 
37
38
  function Builder(options) {
@@ -6,104 +6,126 @@ const EOL = "\n";
6
6
  * @param {any} options
7
7
  * @returns
8
8
  */
9
- function toXml(jArray, options){
10
- return arrToStr( jArray, options, "", 0);
9
+ function toXml(jArray, options) {
10
+ let indentation = "";
11
+ if (options.format && options.indentBy.length > 0) {
12
+ indentation = EOL;
13
+ }
14
+ return arrToStr(jArray, options, "", indentation);
11
15
  }
12
16
 
13
- function arrToStr(arr, options, jPath, level){
17
+ function arrToStr(arr, options, jPath, indentation) {
14
18
  let xmlStr = "";
15
-
16
- let indentation = "";
17
- if(options.format && options.indentBy.length > 0){//TODO: this logic can be avoided for each call
18
- indentation = EOL + "" + options.indentBy.repeat(level);
19
- }
19
+ let isPreviousElementTag = false;
20
20
 
21
21
  for (let i = 0; i < arr.length; i++) {
22
22
  const tagObj = arr[i];
23
23
  const tagName = propName(tagObj);
24
24
  let newJPath = "";
25
- if(jPath.length === 0) newJPath = tagName
25
+ if (jPath.length === 0) newJPath = tagName
26
26
  else newJPath = `${jPath}.${tagName}`;
27
27
 
28
- if(tagName === options.textNodeName){
28
+ if (tagName === options.textNodeName) {
29
29
  let tagText = tagObj[tagName];
30
- if(!isStopNode(newJPath, options)){
31
- tagText = options.tagValueProcessor( tagName, tagText);
30
+ if (!isStopNode(newJPath, options)) {
31
+ tagText = options.tagValueProcessor(tagName, tagText);
32
32
  tagText = replaceEntitiesValue(tagText, options);
33
33
  }
34
- xmlStr += indentation + tagText;
34
+ if (isPreviousElementTag) {
35
+ xmlStr += indentation;
36
+ }
37
+ xmlStr += tagText;
38
+ isPreviousElementTag = false;
35
39
  continue;
36
- }else if( tagName === options.cdataPropName){
37
- xmlStr += indentation + `<![CDATA[${tagObj[tagName][0][options.textNodeName]}]]>`;
40
+ } else if (tagName === options.cdataPropName) {
41
+ if (isPreviousElementTag) {
42
+ xmlStr += indentation;
43
+ }
44
+ xmlStr += `<![CDATA[${tagObj[tagName][0][options.textNodeName]}]]>`;
45
+ isPreviousElementTag = false;
38
46
  continue;
39
- }else if( tagName === options.commentPropName){
47
+ } else if (tagName === options.commentPropName) {
40
48
  xmlStr += indentation + `<!--${tagObj[tagName][0][options.textNodeName]}-->`;
49
+ isPreviousElementTag = true;
41
50
  continue;
42
- }else if( tagName[0] === "?"){
51
+ } else if (tagName[0] === "?") {
43
52
  const attStr = attr_to_str(tagObj[":@"], options);
44
53
  const tempInd = tagName === "?xml" ? "" : indentation;
45
54
  let piTextNodeName = tagObj[tagName][0][options.textNodeName];
46
55
  piTextNodeName = piTextNodeName.length !== 0 ? " " + piTextNodeName : ""; //remove extra spacing
47
56
  xmlStr += tempInd + `<${tagName}${piTextNodeName}${attStr}?>`;
57
+ isPreviousElementTag = true;
48
58
  continue;
49
59
  }
60
+ let newIdentation = indentation;
61
+ if (newIdentation !== "") {
62
+ newIdentation += options.indentBy;
63
+ }
50
64
  const attStr = attr_to_str(tagObj[":@"], options);
51
- let tagStart = indentation + `<${tagName}${attStr}`;
52
- let tagValue = arrToStr(tagObj[tagName], options, newJPath, level + 1);
53
- if(options.unpairedTags.indexOf(tagName) !== -1){
54
- if(options.suppressUnpairedNode) xmlStr += tagStart + ">";
55
- else xmlStr += tagStart + "/>";
56
- }else if( (!tagValue || tagValue.length === 0) && options.suppressEmptyNode){
57
- xmlStr += tagStart + "/>";
58
- }else{
59
- //TODO: node with only text value should not parse the text value in next line
60
- xmlStr += tagStart + `>${tagValue}${indentation}</${tagName}>` ;
65
+ const tagStart = indentation + `<${tagName}${attStr}`;
66
+ const tagValue = arrToStr(tagObj[tagName], options, newJPath, newIdentation);
67
+ if (options.unpairedTags.indexOf(tagName) !== -1) {
68
+ if (options.suppressUnpairedNode) xmlStr += tagStart + ">";
69
+ else xmlStr += tagStart + "/>";
70
+ } else if ((!tagValue || tagValue.length === 0) && options.suppressEmptyNode) {
71
+ xmlStr += tagStart + "/>";
72
+ } else if (tagValue && tagValue.endsWith(">")) {
73
+ xmlStr += tagStart + `>${tagValue}${indentation}</${tagName}>`;
74
+ } else {
75
+ xmlStr += tagStart + ">";
76
+ if (tagValue && indentation !== "" && (tagValue.includes("/>") || tagValue.includes("</"))) {
77
+ xmlStr += indentation + options.indentBy + tagValue + indentation;
78
+ } else {
79
+ xmlStr += tagValue;
80
+ }
81
+ xmlStr += `</${tagName}>`;
61
82
  }
83
+ isPreviousElementTag = true;
62
84
  }
63
-
85
+
64
86
  return xmlStr;
65
87
  }
66
88
 
67
- function propName(obj){
89
+ function propName(obj) {
68
90
  const keys = Object.keys(obj);
69
91
  for (let i = 0; i < keys.length; i++) {
70
- const key = keys[i];
71
- if(key !== ":@") return key;
92
+ const key = keys[i];
93
+ if (key !== ":@") return key;
72
94
  }
73
- }
95
+ }
74
96
 
75
- function attr_to_str(attrMap, options){
97
+ function attr_to_str(attrMap, options) {
76
98
  let attrStr = "";
77
- if(attrMap && !options.ignoreAttributes){
78
- for (let attr in attrMap){
99
+ if (attrMap && !options.ignoreAttributes) {
100
+ for (let attr in attrMap) {
79
101
  let attrVal = options.attributeValueProcessor(attr, attrMap[attr]);
80
102
  attrVal = replaceEntitiesValue(attrVal, options);
81
- if(attrVal === true && options.suppressBooleanAttributes){
82
- attrStr+= ` ${attr.substr(options.attributeNamePrefix.length)}`;
83
- }else{
84
- attrStr+= ` ${attr.substr(options.attributeNamePrefix.length)}="${attrVal}"`;
103
+ if (attrVal === true && options.suppressBooleanAttributes) {
104
+ attrStr += ` ${attr.substr(options.attributeNamePrefix.length)}`;
105
+ } else {
106
+ attrStr += ` ${attr.substr(options.attributeNamePrefix.length)}="${attrVal}"`;
85
107
  }
86
108
  }
87
109
  }
88
110
  return attrStr;
89
111
  }
90
112
 
91
- function isStopNode(jPath, options){
92
- jPath = jPath.substr(0,jPath.length - options.textNodeName.length - 1);
113
+ function isStopNode(jPath, options) {
114
+ jPath = jPath.substr(0, jPath.length - options.textNodeName.length - 1);
93
115
  let tagName = jPath.substr(jPath.lastIndexOf(".") + 1);
94
- for(let index in options.stopNodes){
95
- if(options.stopNodes[index] === jPath || options.stopNodes[index] === "*."+tagName) return true;
116
+ for (let index in options.stopNodes) {
117
+ if (options.stopNodes[index] === jPath || options.stopNodes[index] === "*." + tagName) return true;
96
118
  }
97
119
  return false;
98
120
  }
99
121
 
100
- function replaceEntitiesValue(textValue, options){
101
- if(textValue && textValue.length > 0 && options.processEntities){
102
- for (let i=0; i< options.entities.length; i++) {
103
- const entity = options.entities[i];
104
- textValue = textValue.replace(entity.regex, entity.val);
105
- }
122
+ function replaceEntitiesValue(textValue, options) {
123
+ if (textValue && textValue.length > 0 && options.processEntities) {
124
+ for (let i = 0; i < options.entities.length; i++) {
125
+ const entity = options.entities[i];
126
+ textValue = textValue.replace(entity.regex, entity.val);
127
+ }
106
128
  }
107
129
  return textValue;
108
- }
109
- module.exports = toXml;
130
+ }
131
+ module.exports = toXml;
@@ -32,6 +32,7 @@ const defaultOptions = {
32
32
  ignoreDeclaration: false,
33
33
  ignorePiTags: false,
34
34
  transformTagName: false,
35
+ transformAttributeName: false,
35
36
  };
36
37
 
37
38
  const buildOptions = function(options) {
@@ -132,8 +132,11 @@ function buildAttributesMap(attrStr, jPath) {
132
132
  for (let i = 0; i < len; i++) {
133
133
  const attrName = this.resolveNameSpace(matches[i][1]);
134
134
  let oldVal = matches[i][4];
135
- const aName = this.options.attributeNamePrefix + attrName;
135
+ let aName = this.options.attributeNamePrefix + attrName;
136
136
  if (attrName.length) {
137
+ if (this.options.transformAttributeName) {
138
+ aName = this.options.transformAttributeName(aName);
139
+ }
137
140
  if (oldVal !== undefined) {
138
141
  if (this.options.trimValues) {
139
142
  oldVal = oldVal.trim();
@@ -261,7 +264,7 @@ const parseXml = function(xmlData) {
261
264
 
262
265
  i = closeIndex + 2;
263
266
  }else {//Opening tag
264
- let result = readTagExp(xmlData,i, this. options.removeNSPrefix);
267
+ let result = readTagExp(xmlData,i, this.options.removeNSPrefix);
265
268
  let tagName= result.tagName;
266
269
  let tagExp = result.tagExp;
267
270
  let attrExpPresent = result.attrExpPresent;