@depup/fast-xml-parser 5.5.11-depup.1 → 5.11.0-depup.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 +85 -0
- package/README.md +3 -3
- package/changes.json +4 -4
- 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 +40 -11
- 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 +14 -14
- package/src/fxp.d.ts +52 -17
- package/src/xmlparser/DocTypeReader.js +32 -16
- package/src/xmlparser/OptionsBuilder.js +13 -8
- package/src/xmlparser/OrderedObjParser.js +82 -133
- package/src/xmlparser/XMLParser.js +2 -2
- package/src/xmlparser/node2json.js +4 -0
- package/src/xmlparser/xmlNode.js +14 -0
|
@@ -8,6 +8,9 @@ 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 { EntityDecoder, XML, CURRENCY, COMMON_HTML, ENTITY_ACTION } from '@nodable/entities';
|
|
12
|
+
import { isUnsafe, HTML as HTML_CONTEXT, XML as XML_CONTEXT } from "is-unsafe"
|
|
13
|
+
|
|
11
14
|
|
|
12
15
|
// const regx =
|
|
13
16
|
// '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
|
|
@@ -68,36 +71,10 @@ function extractNamespace(rawTagName) {
|
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
export default class OrderedObjParser {
|
|
71
|
-
constructor(options) {
|
|
74
|
+
constructor(options, externalEntities) {
|
|
72
75
|
this.options = options;
|
|
73
76
|
this.currentNode = null;
|
|
74
77
|
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
78
|
this.parseXml = parseXml;
|
|
102
79
|
this.parseTextData = parseTextData;
|
|
103
80
|
this.resolveNameSpace = resolveNameSpace;
|
|
@@ -110,12 +87,32 @@ export default class OrderedObjParser {
|
|
|
110
87
|
this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)
|
|
111
88
|
this.entityExpansionCount = 0;
|
|
112
89
|
this.currentExpandedLength = 0;
|
|
90
|
+
this.doctypefound = false;
|
|
91
|
+
let namedEntities = { ...XML };
|
|
92
|
+
if (this.options.entityDecoder) {
|
|
93
|
+
this.entityDecoder = this.options.entityDecoder
|
|
94
|
+
} else {
|
|
95
|
+
if (typeof this.options.htmlEntities === "object") namedEntities = this.options.htmlEntities;
|
|
96
|
+
else if (this.options.htmlEntities === true) namedEntities = { ...COMMON_HTML, ...CURRENCY };
|
|
97
|
+
this.entityDecoder = new EntityDecoder({
|
|
98
|
+
namedEntities: { ...namedEntities, ...externalEntities },
|
|
99
|
+
numericAllowed: this.options.htmlEntities,
|
|
100
|
+
limit: {
|
|
101
|
+
maxTotalExpansions: this.options.processEntities.maxTotalExpansions,
|
|
102
|
+
maxExpandedLength: this.options.processEntities.maxExpandedLength,
|
|
103
|
+
applyLimitsTo: this.options.processEntities.appliesTo,
|
|
104
|
+
},
|
|
105
|
+
// onExternalEntity: (name, value) => isUnsafe(value) ? 'block' : 'allow',
|
|
106
|
+
onInputEntity: (name, value) =>
|
|
107
|
+
//TODO: VALID_CONTEXTS.HTML should be set only if this.options.htmlEntities
|
|
108
|
+
isUnsafe(value, [HTML_CONTEXT, XML_CONTEXT]) ? ENTITY_ACTION.BLOCK : ENTITY_ACTION.ALLOW,
|
|
109
|
+
|
|
110
|
+
//postCheck: resolved => resolved
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
113
|
|
|
114
114
|
// Initialize path matcher for path-expression-matcher
|
|
115
115
|
this.matcher = new Matcher();
|
|
116
|
-
|
|
117
|
-
// Live read-only proxy of matcher — PEM creates and caches this internally.
|
|
118
|
-
// All user callbacks receive this instead of the mutable matcher.
|
|
119
116
|
this.readonlyMatcher = this.matcher.readOnly();
|
|
120
117
|
|
|
121
118
|
// Flag to track if current node is a stop node (optimization)
|
|
@@ -141,17 +138,6 @@ export default class OrderedObjParser {
|
|
|
141
138
|
|
|
142
139
|
}
|
|
143
140
|
|
|
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
141
|
|
|
156
142
|
/**
|
|
157
143
|
* @param {string} val
|
|
@@ -212,9 +198,9 @@ function resolveNameSpace(tagname) {
|
|
|
212
198
|
//const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
|
|
213
199
|
const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
|
|
214
200
|
|
|
215
|
-
function buildAttributesMap(attrStr, jPath, tagName) {
|
|
201
|
+
function buildAttributesMap(attrStr, jPath, tagName, force = false) {
|
|
216
202
|
const options = this.options;
|
|
217
|
-
if (options.ignoreAttributes !== true && typeof attrStr === 'string') {
|
|
203
|
+
if (force === true || (options.ignoreAttributes !== true && typeof attrStr === 'string')) {
|
|
218
204
|
// attrStr = attrStr.replace(/\r?\n/g, ' ');
|
|
219
205
|
//attrStr = attrStr || attrStr.trim();
|
|
220
206
|
|
|
@@ -288,7 +274,7 @@ function buildAttributesMap(attrStr, jPath, tagName) {
|
|
|
288
274
|
|
|
289
275
|
if (!hasAttrs) return;
|
|
290
276
|
|
|
291
|
-
if (options.attributesGroupName) {
|
|
277
|
+
if (options.attributesGroupName && !options.preserveOrder) {
|
|
292
278
|
const attrCollection = {};
|
|
293
279
|
attrCollection[options.attributesGroupName] = attrs;
|
|
294
280
|
return attrCollection;
|
|
@@ -304,13 +290,12 @@ const parseXml = function (xmlData) {
|
|
|
304
290
|
|
|
305
291
|
// Reset matcher for new document
|
|
306
292
|
this.matcher.reset();
|
|
293
|
+
this.entityDecoder.reset();
|
|
307
294
|
|
|
308
295
|
// Reset entity expansion counters for this document
|
|
309
296
|
this.entityExpansionCount = 0;
|
|
310
297
|
this.currentExpandedLength = 0;
|
|
311
|
-
this.
|
|
312
|
-
this.lastEntitiesKeys = Object.keys(this.lastEntities);
|
|
313
|
-
this.htmlEntitiesKeys = this.options.htmlEntities ? Object.keys(this.htmlEntities) : [];
|
|
298
|
+
this.doctypefound = false;
|
|
314
299
|
const options = this.options;
|
|
315
300
|
const docTypeReader = new DocTypeReader(options.processEntities);
|
|
316
301
|
const xmlLen = xmlData.length;
|
|
@@ -351,7 +336,12 @@ const parseXml = function (xmlData) {
|
|
|
351
336
|
this.matcher.pop();
|
|
352
337
|
this.isCurrentNodeStopNode = false; // Reset flag when closing tag
|
|
353
338
|
|
|
354
|
-
|
|
339
|
+
//a closing tag with no matching opening tag leaves the stack empty
|
|
340
|
+
currentNode = this.tagsNodeStack.pop() || xmlObj;//avoid recursion, set the parent tag scope
|
|
341
|
+
|
|
342
|
+
if (options.captureMetaData && currentNode) {
|
|
343
|
+
currentNode.addEndIndex(closeIndex + 1);
|
|
344
|
+
}
|
|
355
345
|
textData = "";
|
|
356
346
|
i = closeIndex;
|
|
357
347
|
} else if (c1 === 63) { //'?'
|
|
@@ -360,6 +350,12 @@ const parseXml = function (xmlData) {
|
|
|
360
350
|
if (!tagData) throw new Error("Pi Tag is not closed.");
|
|
361
351
|
|
|
362
352
|
textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
|
|
353
|
+
const attsMap = this.buildAttributesMap(tagData.tagExp, this.matcher, tagData.tagName, true);
|
|
354
|
+
if (attsMap) {
|
|
355
|
+
const ver = attsMap[this.options.attributeNamePrefix + "version"];
|
|
356
|
+
this.entityDecoder.setXmlVersion(Number(ver) || 1.0);
|
|
357
|
+
docTypeReader.setXmlVersion(Number(ver) || 1.0);
|
|
358
|
+
}
|
|
363
359
|
if ((options.ignoreDeclaration && tagData.tagName === "?xml") || options.ignorePiTags) {
|
|
364
360
|
//do nothing
|
|
365
361
|
} else {
|
|
@@ -367,10 +363,15 @@ const parseXml = function (xmlData) {
|
|
|
367
363
|
const childNode = new xmlNode(tagData.tagName);
|
|
368
364
|
childNode.add(options.textNodeName, "");
|
|
369
365
|
|
|
370
|
-
if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) {
|
|
371
|
-
childNode[":@"] =
|
|
366
|
+
if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent && options.ignoreAttributes !== true) {
|
|
367
|
+
childNode[":@"] = attsMap
|
|
372
368
|
}
|
|
373
369
|
this.addChild(currentNode, childNode, this.readonlyMatcher, i);
|
|
370
|
+
|
|
371
|
+
if (options.captureMetaData) {
|
|
372
|
+
// closeIndex points at '?' of the closing '?>'
|
|
373
|
+
currentNode.addEndIndex(tagData.closeIndex + 2);
|
|
374
|
+
}
|
|
374
375
|
}
|
|
375
376
|
|
|
376
377
|
|
|
@@ -389,9 +390,10 @@ const parseXml = function (xmlData) {
|
|
|
389
390
|
i = endIndex;
|
|
390
391
|
} else if (c1 === 33
|
|
391
392
|
&& xmlData.charCodeAt(i + 2) === 68) { //'!D'
|
|
393
|
+
if (this.doctypefound) throw new Error("Multiple DOCTYPE declarations found.");
|
|
394
|
+
this.doctypefound = true;
|
|
392
395
|
const result = docTypeReader.readDocType(xmlData, i);
|
|
393
|
-
this.
|
|
394
|
-
this.docTypeEntitiesKeys = Object.keys(this.docTypeEntities) || []
|
|
396
|
+
this.entityDecoder.addInputEntities(result.entities);
|
|
395
397
|
i = result.i;
|
|
396
398
|
} else if (c1 === 33
|
|
397
399
|
&& xmlData.charCodeAt(i + 2) === 91) { // '!['
|
|
@@ -490,6 +492,7 @@ const parseXml = function (xmlData) {
|
|
|
490
492
|
|
|
491
493
|
if (prefixedAttrs) {
|
|
492
494
|
// Extract raw attributes (without prefix) for our use
|
|
495
|
+
//TODO: seems a performance overhead
|
|
493
496
|
rawAttrs = extractRawAttributes(prefixedAttrs, options);
|
|
494
497
|
}
|
|
495
498
|
}
|
|
@@ -533,6 +536,10 @@ const parseXml = function (xmlData) {
|
|
|
533
536
|
this.isCurrentNodeStopNode = false; // Reset flag
|
|
534
537
|
|
|
535
538
|
this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
|
|
539
|
+
|
|
540
|
+
if (options.captureMetaData) {
|
|
541
|
+
currentNode.addEndIndex(i + 1);
|
|
542
|
+
}
|
|
536
543
|
} else {
|
|
537
544
|
//selfClosing tag
|
|
538
545
|
if (isSelfClosing) {
|
|
@@ -543,6 +550,10 @@ const parseXml = function (xmlData) {
|
|
|
543
550
|
childNode[":@"] = prefixedAttrs;
|
|
544
551
|
}
|
|
545
552
|
this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
|
|
553
|
+
|
|
554
|
+
if (options.captureMetaData) {
|
|
555
|
+
currentNode.addEndIndex(closeIndex + 1);
|
|
556
|
+
}
|
|
546
557
|
this.matcher.pop(); // Pop self-closing tag
|
|
547
558
|
this.isCurrentNodeStopNode = false; // Reset flag
|
|
548
559
|
}
|
|
@@ -552,6 +563,10 @@ const parseXml = function (xmlData) {
|
|
|
552
563
|
childNode[":@"] = prefixedAttrs;
|
|
553
564
|
}
|
|
554
565
|
this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
|
|
566
|
+
|
|
567
|
+
if (options.captureMetaData) {
|
|
568
|
+
currentNode.addEndIndex(result.closeIndex + 1);
|
|
569
|
+
}
|
|
555
570
|
this.matcher.pop(); // Pop unpaired tag
|
|
556
571
|
this.isCurrentNodeStopNode = false; // Reset flag
|
|
557
572
|
i = result.closeIndex;
|
|
@@ -632,78 +647,7 @@ function replaceEntitiesValue(val, tagName, jPath) {
|
|
|
632
647
|
}
|
|
633
648
|
}
|
|
634
649
|
|
|
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;
|
|
650
|
+
return this.entityDecoder.decode(val);
|
|
707
651
|
}
|
|
708
652
|
|
|
709
653
|
|
|
@@ -742,12 +686,16 @@ function isItStopNode() {
|
|
|
742
686
|
* @returns
|
|
743
687
|
*/
|
|
744
688
|
function tagExpWithClosingIndex(xmlData, i, closingChar = ">") {
|
|
689
|
+
//TODO: ignore boolean attributes in tag expression
|
|
690
|
+
//TODO: if ignore attributes, dont read full attribute expression but the end. But read for xml declaration
|
|
745
691
|
let attrBoundary = 0;
|
|
746
|
-
const chars = [];
|
|
747
692
|
const len = xmlData.length;
|
|
748
693
|
const closeCode0 = closingChar.charCodeAt(0);
|
|
749
694
|
const closeCode1 = closingChar.length > 1 ? closingChar.charCodeAt(1) : -1;
|
|
750
695
|
|
|
696
|
+
let result = '';
|
|
697
|
+
let segmentStart = i;
|
|
698
|
+
|
|
751
699
|
for (let index = i; index < len; index++) {
|
|
752
700
|
const code = xmlData.charCodeAt(index);
|
|
753
701
|
|
|
@@ -758,17 +706,18 @@ function tagExpWithClosingIndex(xmlData, i, closingChar = ">") {
|
|
|
758
706
|
} else if (code === closeCode0) {
|
|
759
707
|
if (closeCode1 !== -1) {
|
|
760
708
|
if (xmlData.charCodeAt(index + 1) === closeCode1) {
|
|
761
|
-
|
|
709
|
+
result += xmlData.substring(segmentStart, index);
|
|
710
|
+
return { data: result, index };
|
|
762
711
|
}
|
|
763
712
|
} else {
|
|
764
|
-
|
|
713
|
+
result += xmlData.substring(segmentStart, index);
|
|
714
|
+
return { data: result, index };
|
|
765
715
|
}
|
|
766
|
-
} else if (code === 9) { // \t
|
|
767
|
-
|
|
768
|
-
|
|
716
|
+
} else if (code === 9 && !attrBoundary) { // \t - only replace with space outside attribute values
|
|
717
|
+
// Flush accumulated segment, add space, start new segment
|
|
718
|
+
result += xmlData.substring(segmentStart, index) + ' ';
|
|
719
|
+
segmentStart = index + 1;
|
|
769
720
|
}
|
|
770
|
-
|
|
771
|
-
chars.push(code);
|
|
772
721
|
}
|
|
773
722
|
}
|
|
774
723
|
|
|
@@ -858,7 +807,7 @@ function readStopNodeData(xmlData, tagName, i) {
|
|
|
858
807
|
const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
|
|
859
808
|
i = closeIndex;
|
|
860
809
|
} else {
|
|
861
|
-
const tagData = readTagExp(xmlData, i,
|
|
810
|
+
const tagData = readTagExp(xmlData, i, false)
|
|
862
811
|
|
|
863
812
|
if (tagData) {
|
|
864
813
|
const openTagName = tagData && tagData.tagName;
|
|
@@ -31,8 +31,8 @@ export default class XMLParser {
|
|
|
31
31
|
throw Error(`${result.err.msg}:${result.err.line}:${result.err.col}`)
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
const orderedObjParser = new OrderedObjParser(this.options);
|
|
35
|
-
orderedObjParser.
|
|
34
|
+
const orderedObjParser = new OrderedObjParser(this.options, 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);
|
|
@@ -71,6 +71,10 @@ function compress(arr, options, matcher, readonlyMatcher) {
|
|
|
71
71
|
let val = compress(tagObj[property], options, matcher, readonlyMatcher);
|
|
72
72
|
const isLeaf = isLeafTag(val, options);
|
|
73
73
|
|
|
74
|
+
if (Object.keys(val).length === 0 && options.alwaysCreateTextNode) {
|
|
75
|
+
val[options.textNodeName] = "";
|
|
76
|
+
}
|
|
77
|
+
|
|
74
78
|
if (tagObj[":@"]) {
|
|
75
79
|
assignAttributes(val, tagObj[":@"], readonlyMatcher, options);
|
|
76
80
|
} else if (Object.keys(val).length === 1 && val[options.textNodeName] !== undefined && !options.alwaysCreateTextNode) {
|
package/src/xmlparser/xmlNode.js
CHANGED
|
@@ -27,12 +27,26 @@ export default class XmlNode {
|
|
|
27
27
|
this.child.push({ [node.tagname]: node.child });
|
|
28
28
|
}
|
|
29
29
|
// if requested, add the startIndex
|
|
30
|
+
this.addStartIndex(startIndex);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
addStartIndex(startIndex) {
|
|
30
34
|
if (startIndex !== undefined) {
|
|
31
35
|
// Note: for now we just overwrite the metadata. If we had more complex metadata,
|
|
32
36
|
// we might need to do an object append here: metadata = { ...metadata, startIndex }
|
|
33
37
|
this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex };
|
|
34
38
|
}
|
|
35
39
|
}
|
|
40
|
+
|
|
41
|
+
addEndIndex(endIndex) {
|
|
42
|
+
const lastChild = this.child[this.child.length - 1];
|
|
43
|
+
// endIndex is write-once: when updateTag drops a node, the last child is a
|
|
44
|
+
// previously completed sibling whose endIndex must not be overwritten
|
|
45
|
+
if (lastChild !== undefined && lastChild[METADATA_SYMBOL] !== undefined
|
|
46
|
+
&& lastChild[METADATA_SYMBOL].endIndex === undefined) {
|
|
47
|
+
lastChild[METADATA_SYMBOL].endIndex = endIndex;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
36
50
|
/** symbol used for metadata */
|
|
37
51
|
static getMetaDataSymbol() {
|
|
38
52
|
return METADATA_SYMBOL;
|