fast-xml-parser 4.0.0-beta.8 → 4.0.3

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,29 +1,47 @@
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.0-beta.8 / 2021-12-13**
3
+ **4.0.3 / 2022-02-15**
4
+ * fix: ReferenceError when Bundled with Strict (#431) (By [Andreas Heissenberger](https://github.com/aheissenberger))
5
+
6
+
7
+ **4.0.2 / 2022-02-04**
8
+ * builder supports `suppressUnpairedNode`
9
+ * parser supports `ignoreDeclaration` and `ignorePiTags`
10
+ * fix: when comment is parsed as text value if given as `<!--> ...` #423
11
+ * builder supports decoding `&`
12
+
13
+ **4.0.1 / 2022-01-08**
14
+ * fix builder for pi tag
15
+ * fix: support suppressBooleanAttrs by builder
16
+
17
+ **4.0.0 / 2022-01-06**
18
+ * Generating different combined, parser only, builder only, validator only browser bundles
19
+ * Keeping cjs modules as they can be imported in cjs and esm modules both. Otherwise refer `esm` branch.
20
+
21
+ **4.0.0-beta.8 / 2021-12-13**
4
22
  * call tagValueProcessor for stop nodes
5
23
 
6
- ** 4.0.0-beta.7 / 2021-12-09**
24
+ **4.0.0-beta.7 / 2021-12-09**
7
25
  * fix Validator bug when an attribute has no value but '=' only
8
26
  * XML Builder should suppress unpaired tags by default.
9
27
  * documents update for missing features
10
28
  * refactoring to use Object.assign
11
29
  * refactoring to remove repeated code
12
30
 
13
- ** 4.0.0-beta.6 / 2021-12-05**
31
+ **4.0.0-beta.6 / 2021-12-05**
14
32
  * Support PI Tags processing
15
33
  * Support `suppressBooleanAttributes` by XML Builder for attributes with value `true`.
16
34
 
17
- ** 4.0.0-beta.5 / 2021-12-04**
35
+ **4.0.0-beta.5 / 2021-12-04**
18
36
  * fix: when a tag with name "attributes"
19
37
 
20
- ** 4.0.0-beta.4 / 2021-12-02**
38
+ **4.0.0-beta.4 / 2021-12-02**
21
39
  * Support HTML document parsing
22
40
  * skip stop nodes parsing when building the XML from JS object
23
41
  * Support external entites without DOCTYPE
24
42
  * update dev dependency: strnum v1.0.5 to fix long number issue
25
43
 
26
- ** 4.0.0-beta.3 / 2021-11-30**
44
+ **4.0.0-beta.3 / 2021-11-30**
27
45
  * support global stopNodes expression like "*.stop"
28
46
  * support self-closing and paired unpaired tags
29
47
  * fix: CDATA should not be parsed.
package/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # [fast-xml-parser](https://www.npmjs.com/package/fast-xml-parser)
2
2
  [![Backers on Open Collective](https://opencollective.com/fast-xml-parser/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/fast-xml-parser/sponsors/badge.svg)](#sponsors) [![Known Vulnerabilities](https://snyk.io/test/github/naturalintelligence/fast-xml-parser/badge.svg)](https://snyk.io/test/github/naturalintelligence/fast-xml-parser)
3
3
  [![NPM quality][quality-image]][quality-url]
4
- [![Travis ci Build Status](https://travis-ci.org/NaturalIntelligence/fast-xml-parser.svg?branch=master)](https://travis-ci.org/NaturalIntelligence/fast-xml-parser)
5
4
  [![Coverage Status](https://coveralls.io/repos/github/NaturalIntelligence/fast-xml-parser/badge.svg?branch=master)](https://coveralls.io/github/NaturalIntelligence/fast-xml-parser?branch=master)
6
5
  [<img src="https://img.shields.io/badge/Try-me-blue.svg?colorA=FFA500&colorB=0000FF" alt="Try me"/>](https://naturalintelligence.github.io/fast-xml-parser/)
7
6
  [![NPM total downloads](https://img.shields.io/npm/dt/fast-xml-parser.svg)](https://npm.im/fast-xml-parser)
@@ -90,7 +89,7 @@ const xmlContent = builder.build(jObj);
90
89
 
91
90
  In a HTML page
92
91
  ```html
93
- <script src="path/to/fxparser.js"></script>
92
+ <script src="path/to/fxp.min.js"></script>
94
93
  :
95
94
  <script>
96
95
  const parser = new fxparser.XMLParser();
@@ -98,6 +97,16 @@ In a HTML page
98
97
  </script>
99
98
  ```
100
99
 
100
+ Check lib folder for different browser bundles
101
+
102
+ | Bundle Name | Size |
103
+ | -- | -- |
104
+ | fxbuilder.min.js | 5.2K |
105
+ | fxparser.js | 50K |
106
+ | fxparser.min.js | 17K |
107
+ | fxp.min.js | 22K |
108
+ | fxvalidator.min.js | 5.7K |
109
+
101
110
  ### Documents
102
111
  **v3**
103
112
  * [documents](./docs/v3/docs.md)
@@ -107,9 +116,9 @@ In a HTML page
107
116
  2. [XML Parser](./docs/v4/2.XMLparseOptions.md)
108
117
  3. [XML Builder](./docs/v4/3.XMLBuilder.md)
109
118
  4. [XML Validator](./docs/v4/4.XMLValidator.md)
110
- 5. [Entites](./docs/5.Entities.md)
111
- 6. [HTML Document Parsing](./docs/6.HTMLParsing.md)
112
- 7. [PI Tag processing](./docs/7.PITags.md)
119
+ 5. [Entites](./docs/v4/5.Entities.md)
120
+ 6. [HTML Document Parsing](./docs/v4/6.HTMLParsing.md)
121
+ 7. [PI Tag processing](./docs/v4/7.PITags.md)
113
122
  ## Performance
114
123
 
115
124
  ### XML Parser
@@ -135,6 +144,7 @@ In a HTML page
135
144
  * Run tests for a route or from a route
136
145
  * Customizable reporting
137
146
  * Central dashboard for better monitoring
147
+ * Options to integrate E2E tests with Jira, Github etc using Central dashboard `Tian`.
138
148
  * **[Stubmatic](https://github.com/NaturalIntelligence/Stubmatic)** : Create fake webservices, DynamoDB or S3 servers, Manage fake/mock stub data, Or fake any HTTP(s) call.
139
149
 
140
150
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-xml-parser",
3
- "version": "4.0.0-beta.8",
3
+ "version": "4.0.3",
4
4
  "description": "Validate XML, Parse XML, Build XML without C/C++ based libraries",
5
5
  "main": "./src/fxp.js",
6
6
  "scripts": {
@@ -9,7 +9,7 @@
9
9
  "coverage": "nyc report --reporter html --reporter text -t .nyc_output --report-dir .nyc_output/summary",
10
10
  "perf": "node ./benchmark/perfTest3.js",
11
11
  "lint": "eslint src/*.js spec/*.js",
12
- "bundle": "webpack && webpack --config webpack-prod.config.js",
12
+ "bundle": "webpack --config webpack-prod.config.js",
13
13
  "prettier": "prettier --write src/**/*.js",
14
14
  "publish-please": "publish-please",
15
15
  "checkReadiness": "publish-please --dry-run"
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[];
package/src/fxp_cjs ADDED
@@ -0,0 +1,8 @@
1
+
2
+
3
+ const XMLValidator = require('./validator.js');
4
+ const XMLParser = require('./xmlparser/XMLParser.js');
5
+ const XMLBuilder = require('./xmlbuilder/json2xml.js');
6
+
7
+ module.exports = {XMLParser,XMLValidator, XMLBuilder};
8
+
@@ -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
- ">" : { regex: new RegExp(">", "g"), val: "&gt;" },
26
- "<" : { regex: new RegExp("<", "g"), val: "&lt;" },
27
- "sQuot" : { regex: new RegExp("\'", "g"), val: "&apos;" },
28
- "dQuot" : { regex: new RegExp("\"", "g"), val: "&quot;" }
29
- },
25
+ entities: [
26
+ { regex: new RegExp("&", "g"), val: "&amp;" },//it must be on top
27
+ { regex: new RegExp(">", "g"), val: "&gt;" },
28
+ { regex: new RegExp("<", "g"), val: "&lt;" },
29
+ { regex: new RegExp("\'", "g"), val: "&apos;" },
30
+ { regex: new RegExp("\"", "g"), val: "&quot;" }
31
+ ],
30
32
  processEntities: true,
31
33
  stopNodes: []
32
34
  };
@@ -68,6 +70,7 @@ function Builder(options) {
68
70
  this.buildObjectNode = buildObjectNode;
69
71
 
70
72
  this.replaceEntitiesValue = replaceEntitiesValue;
73
+ this.buildAttrPairStr = buildAttrPairStr;
71
74
  }
72
75
 
73
76
  Builder.prototype.build = function(jObj) {
@@ -90,16 +93,16 @@ Builder.prototype.j2x = function(jObj, level) {
90
93
  if (typeof jObj[key] === 'undefined') {
91
94
  // supress undefined node
92
95
  } else if (jObj[key] === null) {
93
- val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
96
+ if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
97
+ else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
98
+ // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
94
99
  } else if (jObj[key] instanceof Date) {
95
100
  val += this.buildTextNode(jObj[key], key, '', level);
96
101
  } else if (typeof jObj[key] !== 'object') {
97
102
  //premitive type
98
103
  const attr = this.isAttribute(key);
99
104
  if (attr) {
100
- let val = this.options.attributeValueProcessor(attr, '' + jObj[key]);
101
- val = this.replaceEntitiesValue(val);
102
- attrStr += ' ' + attr + '="' + val + '"';
105
+ attrStr += this.buildAttrPairStr(attr, '' + jObj[key]);
103
106
  }else {
104
107
  //tag value
105
108
  if (key === this.options.textNodeName) {
@@ -117,7 +120,9 @@ Builder.prototype.j2x = function(jObj, level) {
117
120
  if (typeof item === 'undefined') {
118
121
  // supress undefined node
119
122
  } else if (item === null) {
120
- val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
123
+ if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
124
+ else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
125
+ // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
121
126
  } else if (typeof item === 'object') {
122
127
  val += this.processTextOrObjNode(item, key, level)
123
128
  } else {
@@ -130,9 +135,7 @@ Builder.prototype.j2x = function(jObj, level) {
130
135
  const Ks = Object.keys(jObj[key]);
131
136
  const L = Ks.length;
132
137
  for (let j = 0; j < L; j++) {
133
- let val = this.options.attributeValueProcessor(Ks[j], '' + jObj[key][Ks[j]]);
134
- val = this.replaceEntitiesValue(val);
135
- attrStr += ' ' + Ks[j] + '="' + val + '"';
138
+ attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]);
136
139
  }
137
140
  } else {
138
141
  val += this.processTextOrObjNode(jObj[key], key, level)
@@ -142,6 +145,14 @@ Builder.prototype.j2x = function(jObj, level) {
142
145
  return {attrStr: attrStr, val: val};
143
146
  };
144
147
 
148
+ function buildAttrPairStr(attrName, val){
149
+ val = this.options.attributeValueProcessor(attrName, '' + val);
150
+ val = this.replaceEntitiesValue(val);
151
+ if (this.options.suppressBooleanAttributes && val === "true") {
152
+ return ' ' + attrName;
153
+ } else return ' ' + attrName + '="' + val + '"';
154
+ }
155
+
145
156
  function processTextOrObjNode (object, key, level) {
146
157
  const result = this.j2x(object, level + 1);
147
158
  if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
@@ -152,34 +163,24 @@ function processTextOrObjNode (object, key, level) {
152
163
  }
153
164
 
154
165
  function buildObjectNode(val, key, attrStr, level) {
166
+ let tagEndExp = '</' + key + this.tagEndChar;
167
+ let piClosingChar = "";
168
+
169
+ if(key[0] === "?") {
170
+ piClosingChar = "?";
171
+ tagEndExp = "";
172
+ }
173
+
155
174
  if (attrStr && val.indexOf('<') === -1) {
156
175
  return (
157
- this.indentate(level) +
158
- '<' +
159
- key +
160
- attrStr +
161
- '>' +
176
+ this.indentate(level) + '<' + key + attrStr + piClosingChar + '>' +
162
177
  val +
163
- //+ this.newLine
164
- // + this.indentate(level)
165
- '</' +
166
- key +
167
- this.tagEndChar
168
- );
178
+ tagEndExp );
169
179
  } else {
170
180
  return (
171
- this.indentate(level) +
172
- '<' +
173
- key +
174
- attrStr +
175
- this.tagEndChar +
181
+ this.indentate(level) + '<' + key + attrStr + piClosingChar + this.tagEndChar +
176
182
  val +
177
- //+ this.newLine
178
- this.indentate(level) +
179
- '</' +
180
- key +
181
- this.tagEndChar
182
- );
183
+ this.indentate(level) + tagEndExp );
183
184
  }
184
185
  }
185
186
 
@@ -187,32 +188,32 @@ function buildEmptyObjNode(val, key, attrStr, level) {
187
188
  if (val !== '') {
188
189
  return this.buildObjectNode(val, key, attrStr, level);
189
190
  } else {
190
- return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
191
- //+ this.newLine
191
+ if(key[0] === "?") return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
192
+ else return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
192
193
  }
193
194
  }
194
195
 
195
196
  function buildTextValNode(val, key, attrStr, level) {
196
- let textValue = this.options.tagValueProcessor(key, val);
197
- textValue = this.replaceEntitiesValue(textValue);
198
-
199
- return (
200
- this.indentate(level) +
201
- '<' +
202
- key +
203
- attrStr +
204
- '>' +
205
- textValue +
206
- '</' +
207
- key +
208
- this.tagEndChar
209
- );
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
+ }
210
211
  }
211
212
 
212
213
  function replaceEntitiesValue(textValue){
213
214
  if(textValue && textValue.length > 0 && this.options.processEntities){
214
- for (const entityName in this.options.entities) {
215
- const entity = this.options.entities[entityName];
215
+ for (let i=0; i<this.options.entities.length; i++) {
216
+ const entity = this.options.entities[i];
216
217
  textValue = textValue.replace(entity.regex, entity.val);
217
218
  }
218
219
  }
@@ -220,12 +221,17 @@ function replaceEntitiesValue(textValue){
220
221
  }
221
222
 
222
223
  function buildEmptyTextNode(val, key, attrStr, level) {
223
- if( val === '' && this.options.unpairedTags.indexOf(key) !== -1){
224
- return this.indentate(level) + '<' + key + attrStr + this.tagEndChar;
225
- }else if (val !== '') {
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
226
231
  return this.buildTextValNode(val, key, attrStr, level);
227
232
  } else {
228
- 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
229
235
  }
230
236
  }
231
237
 
@@ -1,4 +1,4 @@
1
- const {EOL} = require('os');
1
+ const EOL = "\n";
2
2
 
3
3
  /**
4
4
  *
@@ -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{
@@ -71,7 +72,7 @@ function propName(obj){
71
72
  function attr_to_str(attrMap, options){
72
73
  let attrStr = "";
73
74
  if(attrMap && !options.ignoreAttributes){
74
- for( attr in attrMap){
75
+ for (let attr in attrMap){
75
76
  let attrVal = options.attributeValueProcessor(attr, attrMap[attr]);
76
77
  attrVal = replaceEntitiesValue(attrVal, options);
77
78
  if(attrVal === true && options.suppressBooleanAttributes){
@@ -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 (const entityName in options.entities) {
99
- const entity = options.entities[entityName];
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
  }
@@ -29,6 +29,8 @@ const defaultOptions = {
29
29
  unpairedTags: [],
30
30
  processEntities: true,
31
31
  htmlEntities: false,
32
+ ignoreDeclaration: false,
33
+ ignorePiTags: false
32
34
  };
33
35
 
34
36
  const buildOptions = function(options) {
@@ -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
- currentNode.addChild(childNode);
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