fast-xml-parser 5.5.11 → 5.6.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 +10 -0
- package/lib/fxbuilder.min.js +1 -1
- package/lib/fxbuilder.min.js.map +1 -1
- package/lib/fxp.cjs +1 -1
- package/lib/fxp.d.cts +19 -9
- package/lib/fxp.min.js +1 -1
- package/lib/fxp.min.js.map +1 -1
- package/lib/fxparser.min.js +1 -1
- package/lib/fxparser.min.js.map +1 -1
- package/package.json +3 -2
- package/src/fast-xml-parser.zip +0 -0
- package/src/fxp.d.ts +18 -15
- package/src/xmlparser/OrderedObjParser.js +13 -114
- package/src/xmlparser/XMLParser.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-xml-parser",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.6.0",
|
|
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,9 @@
|
|
|
87
87
|
}
|
|
88
88
|
],
|
|
89
89
|
"dependencies": {
|
|
90
|
+
"@nodable/entities": "^1.1.0",
|
|
90
91
|
"fast-xml-builder": "^1.1.4",
|
|
91
|
-
"path-expression-matcher": "^1.
|
|
92
|
+
"path-expression-matcher": "^1.5.0",
|
|
92
93
|
"strnum": "^2.2.3"
|
|
93
94
|
}
|
|
94
95
|
}
|
|
Binary file
|
package/src/fxp.d.ts
CHANGED
|
@@ -66,20 +66,23 @@ export class Expression {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// ---------------------------------------------------------------------------
|
|
69
|
-
//
|
|
69
|
+
// MatcherView
|
|
70
70
|
// ---------------------------------------------------------------------------
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
* A live read-only view of a Matcher instance
|
|
73
|
+
* A lightweight, live read-only view of a Matcher instance.
|
|
74
74
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
75
|
+
* Returned by `Matcher.readOnly()`. The same instance is reused across every
|
|
76
|
+
* callback invocation — no allocation overhead per call. Reads directly from
|
|
77
|
+
* the parent Matcher's internal state so it always reflects the current parser
|
|
78
|
+
* position with no copying or freezing.
|
|
79
|
+
*
|
|
80
|
+
* Mutation methods (`push`, `pop`, `reset`, `updateCurrent`, `restore`) are
|
|
81
|
+
* simply absent — misuse is caught at compile time by TypeScript.
|
|
79
82
|
*
|
|
80
83
|
* This is the type received by all FXP user callbacks when `jPath: false`.
|
|
81
84
|
*/
|
|
82
|
-
export
|
|
85
|
+
export class MatcherView {
|
|
83
86
|
readonly separator: string;
|
|
84
87
|
|
|
85
88
|
/** Check if current path matches an Expression. */
|
|
@@ -111,14 +114,14 @@ export interface ReadonlyMatcher {
|
|
|
111
114
|
|
|
112
115
|
/** Current path as an array of tag names. */
|
|
113
116
|
toArray(): string[];
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Create a snapshot of the current state.
|
|
117
|
-
* The snapshot can be passed to the real Matcher.restore if needed.
|
|
118
|
-
*/
|
|
119
|
-
snapshot(): MatcherSnapshot;
|
|
120
117
|
}
|
|
121
118
|
|
|
119
|
+
/**
|
|
120
|
+
* @deprecated Use {@link MatcherView} instead.
|
|
121
|
+
* Alias kept for backward compatibility.
|
|
122
|
+
*/
|
|
123
|
+
export type ReadonlyMatcher = MatcherView;
|
|
124
|
+
|
|
122
125
|
/** Internal node structure — exposed via snapshot only. */
|
|
123
126
|
export interface PathNode {
|
|
124
127
|
tag: string;
|
|
@@ -141,8 +144,8 @@ export interface MatcherSnapshot {
|
|
|
141
144
|
**********************************************************************/
|
|
142
145
|
|
|
143
146
|
// jPath: true → string
|
|
144
|
-
// jPath: false →
|
|
145
|
-
type JPathOrMatcher = string |
|
|
147
|
+
// jPath: false → MatcherView
|
|
148
|
+
type JPathOrMatcher = string | MatcherView;
|
|
146
149
|
type JPathOrExpression = string | Expression;
|
|
147
150
|
|
|
148
151
|
export type ProcessEntitiesOptions = {
|
|
@@ -8,6 +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
12
|
|
|
12
13
|
// const regx =
|
|
13
14
|
// '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
|
|
@@ -72,32 +73,6 @@ export default class OrderedObjParser {
|
|
|
72
73
|
this.options = options;
|
|
73
74
|
this.currentNode = null;
|
|
74
75
|
this.tagsNodeStack = [];
|
|
75
|
-
this.docTypeEntities = {};
|
|
76
|
-
this.lastEntities = {
|
|
77
|
-
"apos": { regex: /&(apos|#39|#x27);/g, val: "'" },
|
|
78
|
-
"gt": { regex: /&(gt|#62|#x3E);/g, val: ">" },
|
|
79
|
-
"lt": { regex: /&(lt|#60|#x3C);/g, val: "<" },
|
|
80
|
-
"quot": { regex: /&(quot|#34|#x22);/g, val: "\"" },
|
|
81
|
-
};
|
|
82
|
-
this.ampEntity = { regex: /&(amp|#38|#x26);/g, val: "&" };
|
|
83
|
-
this.htmlEntities = {
|
|
84
|
-
"space": { regex: /&(nbsp|#160);/g, val: " " },
|
|
85
|
-
// "lt" : { regex: /&(lt|#60);/g, val: "<" },
|
|
86
|
-
// "gt" : { regex: /&(gt|#62);/g, val: ">" },
|
|
87
|
-
// "amp" : { regex: /&(amp|#38);/g, val: "&" },
|
|
88
|
-
// "quot" : { regex: /&(quot|#34);/g, val: "\"" },
|
|
89
|
-
// "apos" : { regex: /&(apos|#39);/g, val: "'" },
|
|
90
|
-
"cent": { regex: /&(cent|#162);/g, val: "¢" },
|
|
91
|
-
"pound": { regex: /&(pound|#163);/g, val: "£" },
|
|
92
|
-
"yen": { regex: /&(yen|#165);/g, val: "¥" },
|
|
93
|
-
"euro": { regex: /&(euro|#8364);/g, val: "€" },
|
|
94
|
-
"copyright": { regex: /&(copy|#169);/g, val: "©" },
|
|
95
|
-
"reg": { regex: /&(reg|#174);/g, val: "®" },
|
|
96
|
-
"inr": { regex: /&(inr|#8377);/g, val: "₹" },
|
|
97
|
-
"num_dec": { regex: /&#([0-9]{1,7});/g, val: (_, str) => fromCodePoint(str, 10, "&#") },
|
|
98
|
-
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val: (_, str) => fromCodePoint(str, 16, "&#x") },
|
|
99
|
-
};
|
|
100
|
-
this.addExternalEntities = addExternalEntities;
|
|
101
76
|
this.parseXml = parseXml;
|
|
102
77
|
this.parseTextData = parseTextData;
|
|
103
78
|
this.resolveNameSpace = resolveNameSpace;
|
|
@@ -111,6 +86,16 @@ export default class OrderedObjParser {
|
|
|
111
86
|
this.entityExpansionCount = 0;
|
|
112
87
|
this.currentExpandedLength = 0;
|
|
113
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
|
+
});
|
|
98
|
+
|
|
114
99
|
// Initialize path matcher for path-expression-matcher
|
|
115
100
|
this.matcher = new Matcher();
|
|
116
101
|
|
|
@@ -141,17 +126,6 @@ export default class OrderedObjParser {
|
|
|
141
126
|
|
|
142
127
|
}
|
|
143
128
|
|
|
144
|
-
function addExternalEntities(externalEntities) {
|
|
145
|
-
const entKeys = Object.keys(externalEntities);
|
|
146
|
-
for (let i = 0; i < entKeys.length; i++) {
|
|
147
|
-
const ent = entKeys[i];
|
|
148
|
-
const escaped = ent.replace(/[.\-+*:]/g, '\\.');
|
|
149
|
-
this.lastEntities[ent] = {
|
|
150
|
-
regex: new RegExp("&" + escaped + ";", "g"),
|
|
151
|
-
val: externalEntities[ent]
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
129
|
|
|
156
130
|
/**
|
|
157
131
|
* @param {string} val
|
|
@@ -308,9 +282,6 @@ const parseXml = function (xmlData) {
|
|
|
308
282
|
// Reset entity expansion counters for this document
|
|
309
283
|
this.entityExpansionCount = 0;
|
|
310
284
|
this.currentExpandedLength = 0;
|
|
311
|
-
this.docTypeEntitiesKeys = [];
|
|
312
|
-
this.lastEntitiesKeys = Object.keys(this.lastEntities);
|
|
313
|
-
this.htmlEntitiesKeys = this.options.htmlEntities ? Object.keys(this.htmlEntities) : [];
|
|
314
285
|
const options = this.options;
|
|
315
286
|
const docTypeReader = new DocTypeReader(options.processEntities);
|
|
316
287
|
const xmlLen = xmlData.length;
|
|
@@ -390,8 +361,7 @@ const parseXml = function (xmlData) {
|
|
|
390
361
|
} else if (c1 === 33
|
|
391
362
|
&& xmlData.charCodeAt(i + 2) === 68) { //'!D'
|
|
392
363
|
const result = docTypeReader.readDocType(xmlData, i);
|
|
393
|
-
this.
|
|
394
|
-
this.docTypeEntitiesKeys = Object.keys(this.docTypeEntities) || []
|
|
364
|
+
this.entityReplacer.addInputEntities(result.entities);
|
|
395
365
|
i = result.i;
|
|
396
366
|
} else if (c1 === 33
|
|
397
367
|
&& xmlData.charCodeAt(i + 2) === 91) { // '!['
|
|
@@ -632,78 +602,7 @@ function replaceEntitiesValue(val, tagName, jPath) {
|
|
|
632
602
|
}
|
|
633
603
|
}
|
|
634
604
|
|
|
635
|
-
|
|
636
|
-
for (const entityName of this.docTypeEntitiesKeys) {
|
|
637
|
-
const entity = this.docTypeEntities[entityName];
|
|
638
|
-
const matches = val.match(entity.regx);
|
|
639
|
-
|
|
640
|
-
if (matches) {
|
|
641
|
-
// Track expansions
|
|
642
|
-
this.entityExpansionCount += matches.length;
|
|
643
|
-
|
|
644
|
-
// Check expansion limit
|
|
645
|
-
if (entityConfig.maxTotalExpansions &&
|
|
646
|
-
this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
647
|
-
throw new Error(
|
|
648
|
-
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
649
|
-
);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
// Store length before replacement
|
|
653
|
-
const lengthBefore = val.length;
|
|
654
|
-
val = val.replace(entity.regx, entity.val);
|
|
655
|
-
|
|
656
|
-
// Check expanded length immediately after replacement
|
|
657
|
-
if (entityConfig.maxExpandedLength) {
|
|
658
|
-
this.currentExpandedLength += (val.length - lengthBefore);
|
|
659
|
-
|
|
660
|
-
if (this.currentExpandedLength > entityConfig.maxExpandedLength) {
|
|
661
|
-
throw new Error(
|
|
662
|
-
`Total expanded content size exceeded: ${this.currentExpandedLength} > ${entityConfig.maxExpandedLength}`
|
|
663
|
-
);
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
if (val.indexOf('&') === -1) return val;
|
|
669
|
-
// Replace standard entities
|
|
670
|
-
for (const entityName of this.lastEntitiesKeys) {
|
|
671
|
-
const entity = this.lastEntities[entityName];
|
|
672
|
-
const matches = val.match(entity.regex);
|
|
673
|
-
if (matches) {
|
|
674
|
-
this.entityExpansionCount += matches.length;
|
|
675
|
-
if (entityConfig.maxTotalExpansions &&
|
|
676
|
-
this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
677
|
-
throw new Error(
|
|
678
|
-
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
679
|
-
);
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
val = val.replace(entity.regex, entity.val);
|
|
683
|
-
}
|
|
684
|
-
if (val.indexOf('&') === -1) return val;
|
|
685
|
-
|
|
686
|
-
// Replace HTML entities if enabled
|
|
687
|
-
for (const entityName of this.htmlEntitiesKeys) {
|
|
688
|
-
const entity = this.htmlEntities[entityName];
|
|
689
|
-
const matches = val.match(entity.regex);
|
|
690
|
-
if (matches) {
|
|
691
|
-
//console.log(matches);
|
|
692
|
-
this.entityExpansionCount += matches.length;
|
|
693
|
-
if (entityConfig.maxTotalExpansions &&
|
|
694
|
-
this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
695
|
-
throw new Error(
|
|
696
|
-
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
697
|
-
);
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
val = val.replace(entity.regex, entity.val);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// Replace ampersand entity last
|
|
704
|
-
val = val.replace(this.ampEntity.regex, this.ampEntity.val);
|
|
705
|
-
|
|
706
|
-
return val;
|
|
605
|
+
return this.entityReplacer.replace(val);
|
|
707
606
|
}
|
|
708
607
|
|
|
709
608
|
|
|
@@ -32,7 +32,7 @@ export default class XMLParser {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
const orderedObjParser = new OrderedObjParser(this.options);
|
|
35
|
-
orderedObjParser.
|
|
35
|
+
orderedObjParser.entityReplacer.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);
|