fast-xml-parser 4.4.1 → 4.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  <small>Note: If you find missing information about particular minor version, that version must have been changed without any functional change in this library.</small>
2
2
 
3
+ **4.5.0 / 2024-09-03**
4
+ - feat #666: ignoreAttributes support function, and array of string or regex (By [ArtemM](https://github.com/mav-rik))
5
+
3
6
  **4.4.1 / 2024-07-28**
4
7
  - v5 fix: maximum length limit to currency value
5
8
  - fix #634: build attributes with oneListGroup and attributesGroupName (#653)(By [Andreas Naziris](https://github.com/a-rasin))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-xml-parser",
3
- "version": "4.4.1",
3
+ "version": "4.5.0",
4
4
  "description": "Validate XML, Parse XML, Build XML without C/C++ based libraries",
5
5
  "main": "./src/fxp.js",
6
6
  "scripts": {
@@ -49,7 +49,6 @@
49
49
  "@babel/register": "^7.13.8",
50
50
  "@types/node": "20",
51
51
  "babel-loader": "^8.2.2",
52
- "cytorus": "^0.2.9",
53
52
  "eslint": "^8.3.0",
54
53
  "he": "^1.2.0",
55
54
  "jasmine": "^3.6.4",
package/src/fxp.d.ts CHANGED
@@ -30,9 +30,17 @@ type X2jOptions = {
30
30
  /**
31
31
  * Whether to ignore attributes when parsing
32
32
  *
33
+ * When `true` - ignores all the attributes
34
+ *
35
+ * When `false` - parses all the attributes
36
+ *
37
+ * When `Array<string | RegExp>` - filters out attributes that match provided patterns
38
+ *
39
+ * When `Function` - calls the function for each attribute and filters out those for which the function returned `true`
40
+ *
33
41
  * Defaults to `true`
34
42
  */
35
- ignoreAttributes?: boolean;
43
+ ignoreAttributes?: boolean | (string | RegExp)[] | ((attrName: string, jPath: string) => boolean);
36
44
 
37
45
  /**
38
46
  * Whether to remove namespace string from tag and attribute names
@@ -250,11 +258,19 @@ type XmlBuilderOptions = {
250
258
  textNodeName?: string;
251
259
 
252
260
  /**
253
- * Whether to ignore attributes when parsing
261
+ * Whether to ignore attributes when building
262
+ *
263
+ * When `true` - ignores all the attributes
264
+ *
265
+ * When `false` - builds all the attributes
266
+ *
267
+ * When `Array<string | RegExp>` - filters out attributes that match provided patterns
268
+ *
269
+ * When `Function` - calls the function for each attribute and filters out those for which the function returned `true`
254
270
  *
255
271
  * Defaults to `true`
256
272
  */
257
- ignoreAttributes?: boolean;
273
+ ignoreAttributes?: boolean | (string | RegExp)[] | ((attrName: string, jPath: string) => boolean);
258
274
 
259
275
  /**
260
276
  * Give a property name to set CDATA values to instead of merging to tag's text value
@@ -0,0 +1,20 @@
1
+ function getIgnoreAttributesFn(ignoreAttributes) {
2
+ if (typeof ignoreAttributes === 'function') {
3
+ return ignoreAttributes
4
+ }
5
+ if (Array.isArray(ignoreAttributes)) {
6
+ return (attrName) => {
7
+ for (const pattern of ignoreAttributes) {
8
+ if (typeof pattern === 'string' && attrName === pattern) {
9
+ return true
10
+ }
11
+ if (pattern instanceof RegExp && pattern.test(attrName)) {
12
+ return true
13
+ }
14
+ }
15
+ }
16
+ }
17
+ return () => false
18
+ }
19
+
20
+ module.exports = getIgnoreAttributesFn
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
  //parse Empty Node as self closing node
3
3
  const buildFromOrderedJs = require('./orderedJs2Xml');
4
+ const getIgnoreAttributesFn = require('../ignoreAttributes')
4
5
 
5
6
  const defaultOptions = {
6
7
  attributeNamePrefix: '@_',
@@ -38,11 +39,12 @@ const defaultOptions = {
38
39
 
39
40
  function Builder(options) {
40
41
  this.options = Object.assign({}, defaultOptions, options);
41
- if (this.options.ignoreAttributes || this.options.attributesGroupName) {
42
+ if (this.options.ignoreAttributes === true || this.options.attributesGroupName) {
42
43
  this.isAttribute = function(/*a*/) {
43
44
  return false;
44
45
  };
45
46
  } else {
47
+ this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)
46
48
  this.attrPrefixLen = this.options.attributeNamePrefix.length;
47
49
  this.isAttribute = isAttribute;
48
50
  }
@@ -71,13 +73,14 @@ Builder.prototype.build = function(jObj) {
71
73
  [this.options.arrayNodeName] : jObj
72
74
  }
73
75
  }
74
- return this.j2x(jObj, 0).val;
76
+ return this.j2x(jObj, 0, []).val;
75
77
  }
76
78
  };
77
79
 
78
- Builder.prototype.j2x = function(jObj, level) {
80
+ Builder.prototype.j2x = function(jObj, level, ajPath) {
79
81
  let attrStr = '';
80
82
  let val = '';
83
+ const jPath = ajPath.join('.')
81
84
  for (let key in jObj) {
82
85
  if(!Object.prototype.hasOwnProperty.call(jObj, key)) continue;
83
86
  if (typeof jObj[key] === 'undefined') {
@@ -100,9 +103,9 @@ Builder.prototype.j2x = function(jObj, level) {
100
103
  } else if (typeof jObj[key] !== 'object') {
101
104
  //premitive type
102
105
  const attr = this.isAttribute(key);
103
- if (attr) {
106
+ if (attr && !this.ignoreAttributesFn(attr, jPath)) {
104
107
  attrStr += this.buildAttrPairStr(attr, '' + jObj[key]);
105
- }else {
108
+ } else if (!attr) {
106
109
  //tag value
107
110
  if (key === this.options.textNodeName) {
108
111
  let newval = this.options.tagValueProcessor(key, '' + jObj[key]);
@@ -126,13 +129,13 @@ Builder.prototype.j2x = function(jObj, level) {
126
129
  // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
127
130
  } else if (typeof item === 'object') {
128
131
  if(this.options.oneListGroup){
129
- const result = this.j2x(item, level + 1);
132
+ const result = this.j2x(item, level + 1, ajPath.concat(key));
130
133
  listTagVal += result.val;
131
134
  if (this.options.attributesGroupName && item.hasOwnProperty(this.options.attributesGroupName)) {
132
135
  listTagAttr += result.attrStr
133
136
  }
134
137
  }else{
135
- listTagVal += this.processTextOrObjNode(item, key, level)
138
+ listTagVal += this.processTextOrObjNode(item, key, level, ajPath)
136
139
  }
137
140
  } else {
138
141
  if (this.options.oneListGroup) {
@@ -157,7 +160,7 @@ Builder.prototype.j2x = function(jObj, level) {
157
160
  attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]);
158
161
  }
159
162
  } else {
160
- val += this.processTextOrObjNode(jObj[key], key, level)
163
+ val += this.processTextOrObjNode(jObj[key], key, level, ajPath)
161
164
  }
162
165
  }
163
166
  }
@@ -172,8 +175,8 @@ Builder.prototype.buildAttrPairStr = function(attrName, val){
172
175
  } else return ' ' + attrName + '="' + val + '"';
173
176
  }
174
177
 
175
- function processTextOrObjNode (object, key, level) {
176
- const result = this.j2x(object, level + 1);
178
+ function processTextOrObjNode (object, key, level, ajPath) {
179
+ const result = this.j2x(object, level + 1, ajPath.concat(key));
177
180
  if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
178
181
  return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level);
179
182
  } else {
@@ -5,6 +5,7 @@ const util = require('../util');
5
5
  const xmlNode = require('./xmlNode');
6
6
  const readDocType = require("./DocTypeReader");
7
7
  const toNumber = require("strnum");
8
+ const getIgnoreAttributesFn = require('../ignoreAttributes')
8
9
 
9
10
  // const regx =
10
11
  // '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
@@ -53,6 +54,7 @@ class OrderedObjParser{
53
54
  this.readStopNodeData = readStopNodeData;
54
55
  this.saveTextToParentTag = saveTextToParentTag;
55
56
  this.addChild = addChild;
57
+ this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)
56
58
  }
57
59
 
58
60
  }
@@ -125,7 +127,7 @@ function resolveNameSpace(tagname) {
125
127
  const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
126
128
 
127
129
  function buildAttributesMap(attrStr, jPath, tagName) {
128
- if (!this.options.ignoreAttributes && typeof attrStr === 'string') {
130
+ if (this.options.ignoreAttributes !== true && typeof attrStr === 'string') {
129
131
  // attrStr = attrStr.replace(/\r?\n/g, ' ');
130
132
  //attrStr = attrStr || attrStr.trim();
131
133
 
@@ -134,6 +136,9 @@ function buildAttributesMap(attrStr, jPath, tagName) {
134
136
  const attrs = {};
135
137
  for (let i = 0; i < len; i++) {
136
138
  const attrName = this.resolveNameSpace(matches[i][1]);
139
+ if (this.ignoreAttributesFn(attrName, jPath)) {
140
+ continue
141
+ }
137
142
  let oldVal = matches[i][4];
138
143
  let aName = this.options.attributeNamePrefix + attrName;
139
144
  if (attrName.length) {