fast-xml-parser 5.6.0 → 5.7.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-xml-parser",
3
- "version": "5.6.0",
3
+ "version": "5.7.1",
4
4
  "description": "Validate XML, Parse XML, Build XML without C/C++ based libraries",
5
5
  "main": "./lib/fxp.cjs",
6
6
  "type": "module",
@@ -87,8 +87,8 @@
87
87
  }
88
88
  ],
89
89
  "dependencies": {
90
- "@nodable/entities": "^1.1.0",
91
- "fast-xml-builder": "^1.1.4",
90
+ "@nodable/entities": "^2.1.0",
91
+ "fast-xml-builder": "^1.1.5",
92
92
  "path-expression-matcher": "^1.5.0",
93
93
  "strnum": "^2.2.3"
94
94
  }
package/src/fxp.d.ts CHANGED
@@ -211,6 +211,14 @@ export type ProcessEntitiesOptions = {
211
211
  tagFilter?: ((tagName: string, jPathOrMatcher: JPathOrMatcher) => boolean) | null;
212
212
  };
213
213
 
214
+ export type EntityDecoderOptions = {
215
+ setExternalEntities: (entities: Record<string, string>) => void;
216
+ addInputEntities: (entities: Record<string, string>) => void;
217
+ reset: () => void;
218
+ decode: (text: string) => string;
219
+ setXmlVersion: (version: string) => void;
220
+ }
221
+
214
222
  export type X2jOptions = {
215
223
  /**
216
224
  * Preserve the order of tags in resulting JS object
@@ -397,9 +405,14 @@ export type X2jOptions = {
397
405
  * Whether to process HTML entities
398
406
  *
399
407
  * Defaults to `false`
408
+ * @deprecated Use `entityDecoder` instead
400
409
  */
401
410
  htmlEntities?: boolean;
402
411
 
412
+ /**
413
+ * Custom entity decoder
414
+ */
415
+ entityDecoder?: EntityDecoderOptions;
403
416
  /**
404
417
  * Whether to ignore the declaration tag from output
405
418
  *
@@ -707,6 +720,9 @@ export class XMLParser {
707
720
  export class XMLValidator {
708
721
  static validate(xmlData: string, options?: validationOptions): true | ValidationError;
709
722
  }
723
+ /**
724
+ * @deprecated Use npm package 'fast-xml-builder' instead
725
+ */
710
726
  export class XMLBuilder {
711
727
  constructor(options?: XmlBuilderOptions);
712
728
  build(jObj: any): string;
@@ -35,11 +35,8 @@ export default class DocTypeReader {
35
35
  );
36
36
  }
37
37
  //const escaped = entityName.replace(/[.\-+*:]/g, '\\.');
38
- const escaped = entityName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
39
- entities[entityName] = {
40
- regx: RegExp(`&${escaped};`, "g"),
41
- val: val
42
- };
38
+ //const escaped = entityName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
39
+ entities[entityName] = val;
43
40
  entityCount++;
44
41
  }
45
42
  }
@@ -1,4 +1,5 @@
1
1
  import { DANGEROUS_PROPERTY_NAMES, criticalProperties } from "../util.js";
2
+ import { COMMON_HTML, CURRENCY } from '@nodable/entities';
2
3
 
3
4
  const defaultOnDangerousProperty = (name) => {
4
5
  if (DANGEROUS_PROPERTY_NAMES.includes(name)) {
@@ -39,6 +40,7 @@ export const defaultOptions = {
39
40
  unpairedTags: [],
40
41
  processEntities: true,
41
42
  htmlEntities: false,
43
+ entityDecoder: null,
42
44
  ignoreDeclaration: false,
43
45
  ignorePiTags: false,
44
46
  transformTagName: false,
@@ -85,18 +87,19 @@ function validatePropertyName(propertyName, optionName) {
85
87
  * @param {boolean|object} value
86
88
  * @returns {object} Always returns normalized object
87
89
  */
88
- function normalizeProcessEntities(value) {
90
+ function normalizeProcessEntities(value, htmlEntities) {
89
91
  // Boolean backward compatibility
90
92
  if (typeof value === 'boolean') {
91
93
  return {
92
94
  enabled: value, // true or false
93
95
  maxEntitySize: 10000,
94
- maxExpansionDepth: 10,
95
- maxTotalExpansions: 1000,
96
+ maxExpansionDepth: 10000,
97
+ maxTotalExpansions: Infinity,
96
98
  maxExpandedLength: 100000,
97
- maxEntityCount: 100,
99
+ maxEntityCount: 1000,
98
100
  allowedTags: null,
99
- tagFilter: null
101
+ tagFilter: null,
102
+ appliesTo: "all",
100
103
  };
101
104
  }
102
105
 
@@ -110,7 +113,8 @@ function normalizeProcessEntities(value) {
110
113
  maxExpandedLength: Math.max(1, value.maxExpandedLength ?? 100000),
111
114
  maxEntityCount: Math.max(1, value.maxEntityCount ?? 1000),
112
115
  allowedTags: value.allowedTags ?? null,
113
- tagFilter: value.tagFilter ?? null
116
+ tagFilter: value.tagFilter ?? null,
117
+ appliesTo: value.appliesTo ?? "all",
114
118
  };
115
119
  }
116
120
 
@@ -141,7 +145,7 @@ export const buildOptions = function (options) {
141
145
  }
142
146
 
143
147
  // Always normalize processEntities for backward compatibility and validation
144
- built.processEntities = normalizeProcessEntities(built.processEntities);
148
+ built.processEntities = normalizeProcessEntities(built.processEntities, built.htmlEntities);
145
149
  built.unpairedTagsSet = new Set(built.unpairedTags);
146
150
  // Convert old-style stopNodes for backward compatibility
147
151
  if (built.stopNodes && Array.isArray(built.stopNodes)) {
@@ -8,7 +8,7 @@ import toNumber from "strnum";
8
8
  import getIgnoreAttributesFn from "../ignoreAttributes.js";
9
9
  import { Expression, Matcher } from 'path-expression-matcher';
10
10
  import { ExpressionSet } from 'path-expression-matcher';
11
- import EntityReplacer, { COMMON_HTML, NUMERIC_ENTITIES, CURRENCY_ENTITIES } from '@nodable/entities';
11
+ import { EntityDecoder, XML, CURRENCY, COMMON_HTML } from '@nodable/entities';
12
12
 
13
13
  // const regx =
14
14
  // '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
@@ -85,16 +85,23 @@ export default class OrderedObjParser {
85
85
  this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)
86
86
  this.entityExpansionCount = 0;
87
87
  this.currentExpandedLength = 0;
88
-
89
- this.entityReplacer = new EntityReplacer({
90
- default: true,
91
- // amp: true,
92
- system: this.options.htmlEntities ? { ...COMMON_HTML, ...NUMERIC_ENTITIES, ...CURRENCY_ENTITIES } : {},
93
- maxTotalExpansions: this.options.processEntities.maxTotalExpansions,
94
- maxExpandedLength: this.options.processEntities.maxExpandedLength,
95
- applyLimitsTo: "all",
96
- //postCheck: resolved => resolved
97
- });
88
+ let namedEntities = { ...XML };
89
+ if (this.options.entityDecoder) {
90
+ this.entityDecoder = this.options.entityDecoder
91
+ } else {
92
+ if (typeof this.options.htmlEntities === "object") namedEntities = this.options.htmlEntities;
93
+ else if (this.options.htmlEntities === true) namedEntities = { ...COMMON_HTML, ...CURRENCY };
94
+ this.entityDecoder = new EntityDecoder({
95
+ namedEntities: namedEntities,
96
+ numericAllowed: this.options.htmlEntities,
97
+ limit: {
98
+ maxTotalExpansions: this.options.processEntities.maxTotalExpansions,
99
+ maxExpandedLength: this.options.processEntities.maxExpandedLength,
100
+ applyLimitsTo: this.options.processEntities.appliesTo,
101
+ }
102
+ //postCheck: resolved => resolved
103
+ });
104
+ }
98
105
 
99
106
  // Initialize path matcher for path-expression-matcher
100
107
  this.matcher = new Matcher();
@@ -186,9 +193,9 @@ function resolveNameSpace(tagname) {
186
193
  //const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
187
194
  const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
188
195
 
189
- function buildAttributesMap(attrStr, jPath, tagName) {
196
+ function buildAttributesMap(attrStr, jPath, tagName, force = false) {
190
197
  const options = this.options;
191
- if (options.ignoreAttributes !== true && typeof attrStr === 'string') {
198
+ if (force === true || (options.ignoreAttributes !== true && typeof attrStr === 'string')) {
192
199
  // attrStr = attrStr.replace(/\r?\n/g, ' ');
193
200
  //attrStr = attrStr || attrStr.trim();
194
201
 
@@ -278,6 +285,7 @@ const parseXml = function (xmlData) {
278
285
 
279
286
  // Reset matcher for new document
280
287
  this.matcher.reset();
288
+ this.entityDecoder.reset();
281
289
 
282
290
  // Reset entity expansion counters for this document
283
291
  this.entityExpansionCount = 0;
@@ -331,6 +339,11 @@ const parseXml = function (xmlData) {
331
339
  if (!tagData) throw new Error("Pi Tag is not closed.");
332
340
 
333
341
  textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
342
+ const attsMap = this.buildAttributesMap(tagData.tagExp, this.matcher, tagData.tagName, true);
343
+ if (attsMap) {
344
+ const ver = attsMap[this.options.attributeNamePrefix + "version"];
345
+ this.entityDecoder.setXmlVersion(Number(ver) || 1.0);
346
+ }
334
347
  if ((options.ignoreDeclaration && tagData.tagName === "?xml") || options.ignorePiTags) {
335
348
  //do nothing
336
349
  } else {
@@ -338,8 +351,8 @@ const parseXml = function (xmlData) {
338
351
  const childNode = new xmlNode(tagData.tagName);
339
352
  childNode.add(options.textNodeName, "");
340
353
 
341
- if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) {
342
- childNode[":@"] = this.buildAttributesMap(tagData.tagExp, this.matcher, tagData.tagName);
354
+ if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent && options.ignoreAttributes !== true) {
355
+ childNode[":@"] = attsMap
343
356
  }
344
357
  this.addChild(currentNode, childNode, this.readonlyMatcher, i);
345
358
  }
@@ -361,7 +374,7 @@ const parseXml = function (xmlData) {
361
374
  } else if (c1 === 33
362
375
  && xmlData.charCodeAt(i + 2) === 68) { //'!D'
363
376
  const result = docTypeReader.readDocType(xmlData, i);
364
- this.entityReplacer.addInputEntities(result.entities);
377
+ this.entityDecoder.addInputEntities(result.entities);
365
378
  i = result.i;
366
379
  } else if (c1 === 33
367
380
  && xmlData.charCodeAt(i + 2) === 91) { // '!['
@@ -460,6 +473,7 @@ const parseXml = function (xmlData) {
460
473
 
461
474
  if (prefixedAttrs) {
462
475
  // Extract raw attributes (without prefix) for our use
476
+ //TODO: seems a performance overhead
463
477
  rawAttrs = extractRawAttributes(prefixedAttrs, options);
464
478
  }
465
479
  }
@@ -602,7 +616,7 @@ function replaceEntitiesValue(val, tagName, jPath) {
602
616
  }
603
617
  }
604
618
 
605
- return this.entityReplacer.replace(val);
619
+ return this.entityDecoder.decode(val);
606
620
  }
607
621
 
608
622
 
@@ -32,7 +32,7 @@ export default class XMLParser {
32
32
  }
33
33
  }
34
34
  const orderedObjParser = new OrderedObjParser(this.options);
35
- orderedObjParser.entityReplacer.setExternalEntities(this.externalEntities);
35
+ orderedObjParser.entityDecoder.setExternalEntities(this.externalEntities);
36
36
  const orderedResult = orderedObjParser.parseXml(xmlData);
37
37
  if (this.options.preserveOrder || orderedResult === undefined) return orderedResult;
38
38
  else return prettify(orderedResult, this.options, orderedObjParser.matcher, orderedObjParser.readonlyMatcher);
Binary file