fast-xml-parser 3.17.5 → 3.20.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/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Modified MIT License
1
+ MIT License
2
2
 
3
3
  Copyright (c) 2017 Amit Kumar Gupta
4
4
 
@@ -9,16 +9,6 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
10
10
  furnished to do so, subject to the following conditions:
11
11
 
12
- If you use this library then you give us the rights to mention your
13
- company/project name and logo in user's list without further permission required.
14
- However, you can request to remove it when you're not the user of this library anymore.
15
- We'll do the necessary changes within 30 days from the notice.
16
-
17
- You're not allowed to publish the code of this library without giving credits to
18
- the original author/maintainers or without permission. You're required to take
19
- the permission from original author to remove any non-code changes which are purposefully
20
- added to this project including pre/post install message, notification on README, etc.
21
-
22
12
  The above copyright notice and this permission notice shall be included in all
23
13
  copies or substantial portions of the Software.
24
14
 
package/README.md CHANGED
@@ -83,8 +83,7 @@ List of some applications/projects using Fast XML Parser. (Raise an issue to sub
83
83
  <a href="http://www.magento.com/" title="Magento" > <img src="https://avatars2.githubusercontent.com/u/168457" width="80px" ></a>
84
84
 
85
85
 
86
-
87
-
86
+ 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.
88
87
 
89
88
  ### Main Features
90
89
 
@@ -139,6 +138,11 @@ var options = {
139
138
  cdataTagName: "__cdata", //default is 'false'
140
139
  cdataPositionChar: "\\c",
141
140
  parseTrueNumberOnly: false,
141
+ numParseOptions:{
142
+ hex: true,
143
+ leadingZeros: true,
144
+ //skipLike: /\+[0-9]{10}/
145
+ }
142
146
  arrayMode: false, //"strict"
143
147
  attrValueProcessor: (val, attrName) => he.decode(val, {isAttributeValue: true}),//default is a=>a
144
148
  tagValueProcessor : (val, tagName) => he.decode(val), //default is a=>a
@@ -193,7 +197,7 @@ Validator returns the following object in case of error;
193
197
  * **cdataTagName** : If specified, parser parse CDATA as nested tag instead of adding it's value to parent tag.
194
198
  * **cdataPositionChar** : It'll help to covert JSON back to XML without losing CDATA position.
195
199
  * **parseTrueNumberOnly**: if true then values like "+123", or "0123" will not be parsed as number.
196
- * **arrayMode** : When `false`, a tag with single occurrence is parsed as an object but as an array in case of multiple occurences. When `true`, a tag will be parsed as an array always excluding leaf nodes. When `strict`, all the tags will be parsed as array only.
200
+ * **arrayMode** : When `false`, a tag with single occurrence is parsed as an object but as an array in case of multiple occurences. When `true`, a tag will be parsed as an array always excluding leaf nodes. When `strict`, all the tags will be parsed as array only. When instance of `RegEx`, only tags will be parsed as array that match the regex. When `function` a tag name is passed to the callback that can be checked.
197
201
  * **tagValueProcessor** : Process tag value during transformation. Like HTML decoding, word capitalization, etc. Applicable in case of string only.
198
202
  * **attrValueProcessor** : Process attribute value during transformation. Like HTML decoding, word capitalization, etc. Applicable in case of string only.
199
203
  * **stopNodes** : an array of tag names which are not required to be parsed. Instead their values are parsed as string.
@@ -347,3 +351,7 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
347
351
  <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>
348
352
  <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>
349
353
  <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>
354
+
355
+ # License
356
+
357
+ * MIT License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-xml-parser",
3
- "version": "3.17.5",
3
+ "version": "3.20.0",
4
4
  "description": "Validate XML or Parse XML to JS/JSON very fast without C/C++ based libraries",
5
5
  "main": "./src/parser.js",
6
6
  "scripts": {
@@ -11,7 +11,6 @@
11
11
  "bundle": "webpack && webpack --config webpack-prod.config.js",
12
12
  "coverage": "istanbul cover -x \"cli.js\" -x \"spec/*spec.js\" jasmine spec/*spec.js;",
13
13
  "coverage:check": "istanbul check-coverage --branch 90 --statement 90",
14
- "postinstall": "node tasks/postinstall.js || exit 0",
15
14
  "prettier": "prettier --write src/**/*.js",
16
15
  "publish-please": "publish-please",
17
16
  "prepublishOnly": "publish-please guard"
@@ -68,27 +67,30 @@
68
67
  "url": "https://github.com/Tatsh"
69
68
  }
70
69
  ],
71
- "license": "MIT Modified",
70
+ "license": "MIT",
72
71
  "devDependencies": {
73
- "@babel/core": "^7.12.9",
74
- "@babel/plugin-transform-runtime": "^7.12.1",
75
- "@babel/preset-env": "^7.12.7",
76
- "@babel/register": "^7.12.1",
72
+ "@babel/core": "^7.13.10",
73
+ "@babel/plugin-transform-runtime": "^7.13.10",
74
+ "@babel/preset-env": "^7.13.10",
75
+ "@babel/register": "^7.13.8",
77
76
  "babel-loader": "^8.2.2",
78
77
  "eslint": "^5.16.0",
79
78
  "he": "^1.2.0",
80
79
  "http-server": "^0.12.3",
81
80
  "istanbul": "^0.4.5",
82
- "jasmine": "^3.6.3",
81
+ "jasmine": "^3.6.4",
83
82
  "nimnjs": "^1.3.2",
84
83
  "prettier": "^1.19.1",
85
84
  "publish-please": "^5.5.2",
86
- "webpack": "^4.44.2",
85
+ "webpack": "^4.46.0",
87
86
  "webpack-cli": "^3.3.12"
88
87
  },
89
88
  "typings": "src/parser.d.ts",
90
89
  "funding": {
91
90
  "type": "paypal",
92
91
  "url": "https://paypal.me/naturalintelligence"
92
+ },
93
+ "dependencies": {
94
+ "strnum": "^1.0.3"
93
95
  }
94
96
  }
package/src/node2json.js CHANGED
@@ -2,47 +2,36 @@
2
2
 
3
3
  const util = require('./util');
4
4
 
5
- const convertToJson = function(node, options) {
5
+ const convertToJson = function(node, options, parentTagName) {
6
6
  const jObj = {};
7
7
 
8
- //when no child node or attr is present
8
+ // when no child node or attr is present
9
9
  if ((!node.child || util.isEmptyObject(node.child)) && (!node.attrsMap || util.isEmptyObject(node.attrsMap))) {
10
10
  return util.isExist(node.val) ? node.val : '';
11
- } else {
12
- //otherwise create a textnode if node has some text
13
- if (util.isExist(node.val)) {
14
- if (!(typeof node.val === 'string' && (node.val === '' || node.val === options.cdataPositionChar))) {
15
- if(options.arrayMode === "strict"){
16
- jObj[options.textNodeName] = [ node.val ];
17
- }else{
18
- jObj[options.textNodeName] = node.val;
19
- }
20
- }
21
- }
11
+ }
12
+
13
+ // otherwise create a textnode if node has some text
14
+ if (util.isExist(node.val) && !(typeof node.val === 'string' && (node.val === '' || node.val === options.cdataPositionChar))) {
15
+ const asArray = util.isTagNameInArrayMode(node.tagname, options.arrayMode, parentTagName)
16
+ jObj[options.textNodeName] = asArray ? [node.val] : node.val;
22
17
  }
23
18
 
24
19
  util.merge(jObj, node.attrsMap, options.arrayMode);
25
20
 
26
21
  const keys = Object.keys(node.child);
27
22
  for (let index = 0; index < keys.length; index++) {
28
- var tagname = keys[index];
29
- if (node.child[tagname] && node.child[tagname].length > 1) {
30
- jObj[tagname] = [];
31
- for (var tag in node.child[tagname]) {
32
- jObj[tagname].push(convertToJson(node.child[tagname][tag], options));
23
+ const tagName = keys[index];
24
+ if (node.child[tagName] && node.child[tagName].length > 1) {
25
+ jObj[tagName] = [];
26
+ for (let tag in node.child[tagName]) {
27
+ if (node.child[tagName].hasOwnProperty(tag)) {
28
+ jObj[tagName].push(convertToJson(node.child[tagName][tag], options, tagName));
29
+ }
33
30
  }
34
31
  } else {
35
- if(options.arrayMode === true){
36
- const result = convertToJson(node.child[tagname][0], options)
37
- if(typeof result === 'object')
38
- jObj[tagname] = [ result ];
39
- else
40
- jObj[tagname] = result;
41
- }else if(options.arrayMode === "strict"){
42
- jObj[tagname] = [convertToJson(node.child[tagname][0], options) ];
43
- }else{
44
- jObj[tagname] = convertToJson(node.child[tagname][0], options);
45
- }
32
+ const result = convertToJson(node.child[tagName][0], options, tagName);
33
+ const asArray = (options.arrayMode === true && typeof result === 'object') || util.isTagNameInArrayMode(tagName, options.arrayMode, parentTagName);
34
+ jObj[tagName] = asArray ? [result] : result;
46
35
  }
47
36
  }
48
37
 
package/src/parser.d.ts CHANGED
@@ -7,15 +7,21 @@ type X2jOptions = {
7
7
  allowBooleanAttributes: boolean;
8
8
  parseNodeValue: boolean;
9
9
  parseAttributeValue: boolean;
10
- arrayMode: boolean | 'strict';
10
+ arrayMode: boolean | 'strict' | RegExp | ((tagName: string, parentTagName: string) => boolean);
11
11
  trimValues: boolean;
12
12
  cdataTagName: false | string;
13
13
  cdataPositionChar: string;
14
14
  parseTrueNumberOnly: boolean;
15
+ numParseOptions: strnumOptions;
15
16
  tagValueProcessor: (tagValue: string, tagName: string) => string;
16
17
  attrValueProcessor: (attrValue: string, attrName: string) => string;
17
18
  stopNodes: string[];
18
19
  };
20
+ type strnumOptions = {
21
+ hex: boolean;
22
+ leadingZeros: boolean,
23
+ skipLike: RegExp
24
+ }
19
25
  type X2jOptionsOptional = Partial<X2jOptions>;
20
26
  type validationOptions = {
21
27
  allowBooleanAttributes: boolean;
package/src/parser.js CHANGED
@@ -6,7 +6,7 @@ const x2xmlnode = require('./xmlstr2xmlnode');
6
6
  const buildOptions = require('./util').buildOptions;
7
7
  const validator = require('./validator');
8
8
 
9
- exports.parse = function(xmlData, options, validationOption) {
9
+ exports.parse = function(xmlData, givenOptions = {}, validationOption) {
10
10
  if( validationOption){
11
11
  if(validationOption === true) validationOption = {}
12
12
 
@@ -15,12 +15,21 @@ exports.parse = function(xmlData, options, validationOption) {
15
15
  throw Error( result.err.msg)
16
16
  }
17
17
  }
18
- options = buildOptions(options, x2xmlnode.defaultOptions, x2xmlnode.props);
18
+ if(givenOptions.parseTrueNumberOnly
19
+ && givenOptions.parseNodeValue !== false
20
+ && !givenOptions.numParseOptions){
21
+
22
+ givenOptions.numParseOptions = {
23
+ leadingZeros: false,
24
+ }
25
+ }
26
+ let options = buildOptions(givenOptions, x2xmlnode.defaultOptions, x2xmlnode.props);
27
+
19
28
  const traversableObj = xmlToNodeobj.getTraversalObj(xmlData, options)
20
29
  //print(traversableObj, " ");
21
30
  return nodeToJson.convertToJson(traversableObj, options);
22
31
  };
23
- exports.convertTonimn = require('../src/nimndata').convert2nimn;
32
+ exports.convertTonimn = require('./nimndata').convert2nimn;
24
33
  exports.getTraversalObj = xmlToNodeobj.getTraversalObj;
25
34
  exports.convertToJson = nodeToJson.convertToJson;
26
35
  exports.convertToJsonString = require('./node2json_str').convertToJsonString;
@@ -64,4 +73,4 @@ function print(xmlNode, indentation){
64
73
  }
65
74
  console.log(indentation + "},")
66
75
  }
67
- }
76
+ }
package/src/util.js CHANGED
@@ -43,9 +43,9 @@ exports.merge = function(target, a, arrayMode) {
43
43
  const keys = Object.keys(a); // will return an array of own properties
44
44
  const len = keys.length; //don't make it inline
45
45
  for (let i = 0; i < len; i++) {
46
- if(arrayMode === 'strict'){
46
+ if (arrayMode === 'strict') {
47
47
  target[keys[i]] = [ a[keys[i]] ];
48
- }else{
48
+ } else {
49
49
  target[keys[i]] = a[keys[i]];
50
50
  }
51
51
  }
@@ -82,6 +82,26 @@ exports.buildOptions = function(options, defaultOptions, props) {
82
82
  return newOptions;
83
83
  };
84
84
 
85
+ /**
86
+ * Check if a tag name should be treated as array
87
+ *
88
+ * @param tagName the node tagname
89
+ * @param arrayMode the array mode option
90
+ * @param parentTagName the parent tag name
91
+ * @returns {boolean} true if node should be parsed as array
92
+ */
93
+ exports.isTagNameInArrayMode = function (tagName, arrayMode, parentTagName) {
94
+ if (arrayMode === false) {
95
+ return false;
96
+ } else if (arrayMode instanceof RegExp) {
97
+ return arrayMode.test(tagName);
98
+ } else if (typeof arrayMode === 'function') {
99
+ return !!arrayMode(tagName, parentTagName);
100
+ }
101
+
102
+ return arrayMode === "strict";
103
+ }
104
+
85
105
  exports.isName = isName;
86
106
  exports.getAllMatches = getAllMatches;
87
107
  exports.nameRegexp = nameRegexp;
package/src/validator.js CHANGED
@@ -27,17 +27,18 @@ exports.validate = function (xmlData, options) {
27
27
  }
28
28
 
29
29
  for (let i = 0; i < xmlData.length; i++) {
30
- if (xmlData[i] === '<') {
30
+
31
+ if (xmlData[i] === '<' && xmlData[i+1] === '?') {
32
+ i+=2;
33
+ i = readPI(xmlData,i);
34
+ if (i.err) return i;
35
+ }else if (xmlData[i] === '<') {
31
36
  //starting of tag
32
37
  //read until you reach to '>' avoiding any '>' in attribute value
33
38
 
34
39
  i++;
35
- if (xmlData[i] === '?') {
36
- i = readPI(xmlData, ++i);
37
- if (i.err) {
38
- return i;
39
- }
40
- } else if (xmlData[i] === '!') {
40
+
41
+ if (xmlData[i] === '!') {
41
42
  i = readCommentAndCDATA(xmlData, i);
42
43
  continue;
43
44
  } else {
@@ -140,7 +141,10 @@ exports.validate = function (xmlData, options) {
140
141
  i++;
141
142
  i = readCommentAndCDATA(xmlData, i);
142
143
  continue;
143
- } else {
144
+ } else if (xmlData[i+1] === '?') {
145
+ i = readPI(xmlData, ++i);
146
+ if (i.err) return i;
147
+ } else{
144
148
  break;
145
149
  }
146
150
  } else if (xmlData[i] === '&') {
@@ -3,6 +3,8 @@
3
3
  const util = require('./util');
4
4
  const buildOptions = require('./util').buildOptions;
5
5
  const xmlNode = require('./xmlNode');
6
+ const toNumber = require("strnum");
7
+
6
8
  const regx =
7
9
  '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
8
10
  .replace(/NAME/g, util.nameRegexp);
@@ -32,6 +34,10 @@ const defaultOptions = {
32
34
  trimValues: true, //Trim string values of tag and attributes
33
35
  cdataTagName: false,
34
36
  cdataPositionChar: '\\c',
37
+ numParseOptions: {
38
+ hex: true,
39
+ leadingZeros: true
40
+ },
35
41
  tagValueProcessor: function(a, tagName) {
36
42
  return a;
37
43
  },
@@ -60,6 +66,7 @@ const props = [
60
66
  'tagValueProcessor',
61
67
  'attrValueProcessor',
62
68
  'parseTrueNumberOnly',
69
+ 'numParseOptions',
63
70
  'stopNodes'
64
71
  ];
65
72
  exports.props = props;
@@ -76,7 +83,7 @@ function processTagValue(tagName, val, options) {
76
83
  val = val.trim();
77
84
  }
78
85
  val = options.tagValueProcessor(val, tagName);
79
- val = parseValue(val, options.parseNodeValue, options.parseTrueNumberOnly);
86
+ val = parseValue(val, options.parseNodeValue, options.numParseOptions);
80
87
  }
81
88
 
82
89
  return val;
@@ -96,26 +103,13 @@ function resolveNameSpace(tagname, options) {
96
103
  return tagname;
97
104
  }
98
105
 
99
- function parseValue(val, shouldParse, parseTrueNumberOnly) {
106
+ function parseValue(val, shouldParse, options) {
100
107
  if (shouldParse && typeof val === 'string') {
101
- let parsed;
102
- if (val.trim() === '' || isNaN(val)) {
103
- parsed = val === 'true' ? true : val === 'false' ? false : val;
104
- } else {
105
- if (val.indexOf('0x') !== -1) {
106
- //support hexa decimal
107
- parsed = Number.parseInt(val, 16);
108
- } else if (val.indexOf('.') !== -1) {
109
- parsed = Number.parseFloat(val);
110
- val = val.replace(/\.?0+$/, "");
111
- } else {
112
- parsed = Number.parseInt(val, 10);
113
- }
114
- if (parseTrueNumberOnly) {
115
- parsed = String(parsed) === val ? parsed : val;
116
- }
117
- }
118
- return parsed;
108
+ //console.log(options)
109
+ const newval = val.trim();
110
+ if(newval === 'true' ) return true;
111
+ else if(newval === 'false' ) return false;
112
+ else return toNumber(val, options);
119
113
  } else {
120
114
  if (util.isExist(val)) {
121
115
  return val;
@@ -148,7 +142,7 @@ function buildAttributesMap(attrStr, options) {
148
142
  attrs[options.attributeNamePrefix + attrName] = parseValue(
149
143
  matches[i][4],
150
144
  options.parseAttributeValue,
151
- options.parseTrueNumberOnly
145
+ options.numParseOptions
152
146
  );
153
147
  } else if (options.allowBooleanAttributes) {
154
148
  attrs[options.attributeNamePrefix + attrName] = true;
@@ -253,6 +247,7 @@ const getTraversalObj = function(xmlData, options) {
253
247
  const closeIndex = result.index;
254
248
  const separatorIndex = tagExp.indexOf(" ");
255
249
  let tagName = tagExp;
250
+ let shouldBuildAttributesMap = true;
256
251
  if(separatorIndex !== -1){
257
252
  tagName = tagExp.substr(0, separatorIndex).replace(/\s\s*$/, '');
258
253
  tagExp = tagExp.substr(separatorIndex + 1);
@@ -262,6 +257,7 @@ const getTraversalObj = function(xmlData, options) {
262
257
  const colonIndex = tagName.indexOf(":");
263
258
  if(colonIndex !== -1){
264
259
  tagName = tagName.substr(colonIndex+1);
260
+ shouldBuildAttributesMap = tagName !== result.data.substr(colonIndex + 1);
265
261
  }
266
262
  }
267
263
 
@@ -292,7 +288,7 @@ const getTraversalObj = function(xmlData, options) {
292
288
  if (options.stopNodes.length && options.stopNodes.includes(childNode.tagname)) {
293
289
  childNode.startIndex=closeIndex;
294
290
  }
295
- if(tagName !== tagExp){
291
+ if(tagName !== tagExp && shouldBuildAttributesMap){
296
292
  childNode.attrsMap = buildAttributesMap(tagExp, options);
297
293
  }
298
294
  currentNode.addChild(childNode);
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
-
4
- const msg = '\u001b[96m\u001b[1mLove fast-xml-parser? Check \u001b[32mhttps://amitkumargupta.work \u001b[96m\u001b[1mfor more projects and contribution.\u001b[0m\n';
5
- console.log(msg)