@depup/fast-xml-parser 5.5.6-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 +752 -0
- package/LICENSE +21 -0
- package/README.md +31 -0
- package/changes.json +10 -0
- package/lib/fxbuilder.min.js +2 -0
- package/lib/fxbuilder.min.js.map +1 -0
- package/lib/fxp.cjs +1 -0
- package/lib/fxp.d.cts +595 -0
- package/lib/fxp.min.js +2 -0
- package/lib/fxp.min.js.map +1 -0
- package/lib/fxparser.min.js +2 -0
- package/lib/fxparser.min.js.map +1 -0
- package/lib/fxvalidator.min.js +2 -0
- package/lib/fxvalidator.min.js.map +1 -0
- package/package.json +112 -0
- package/src/cli/cli.js +97 -0
- package/src/cli/man.js +17 -0
- package/src/cli/read.js +43 -0
- package/src/fxp.d.ts +577 -0
- package/src/fxp.js +14 -0
- package/src/ignoreAttributes.js +18 -0
- package/src/util.js +61 -0
- package/src/v6/CharsSymbol.js +16 -0
- package/src/v6/EntitiesParser.js +106 -0
- package/src/v6/OptionsBuilder.js +61 -0
- package/src/v6/OutputBuilders/BaseOutputBuilder.js +69 -0
- package/src/v6/OutputBuilders/JsArrBuilder.js +103 -0
- package/src/v6/OutputBuilders/JsMinArrBuilder.js +100 -0
- package/src/v6/OutputBuilders/JsObjBuilder.js +154 -0
- package/src/v6/OutputBuilders/ParserOptionsBuilder.js +94 -0
- package/src/v6/Report.js +0 -0
- package/src/v6/TagPath.js +81 -0
- package/src/v6/TagPathMatcher.js +13 -0
- package/src/v6/XMLParser.js +83 -0
- package/src/v6/Xml2JsParser.js +235 -0
- package/src/v6/XmlPartReader.js +210 -0
- package/src/v6/XmlSpecialTagsReader.js +111 -0
- package/src/v6/inputSource/BufferSource.js +116 -0
- package/src/v6/inputSource/StringSource.js +121 -0
- package/src/v6/valueParsers/EntitiesParser.js +105 -0
- package/src/v6/valueParsers/booleanParser.js +22 -0
- package/src/v6/valueParsers/booleanParserExt.js +19 -0
- package/src/v6/valueParsers/currency.js +38 -0
- package/src/v6/valueParsers/join.js +13 -0
- package/src/v6/valueParsers/number.js +14 -0
- package/src/v6/valueParsers/trim.js +6 -0
- package/src/validator.js +425 -0
- package/src/xmlbuilder/json2xml.js +6 -0
- package/src/xmlparser/DocTypeReader.js +401 -0
- package/src/xmlparser/OptionsBuilder.js +159 -0
- package/src/xmlparser/OrderedObjParser.js +905 -0
- package/src/xmlparser/XMLParser.js +71 -0
- package/src/xmlparser/node2json.js +174 -0
- package/src/xmlparser/xmlNode.js +40 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { buildOptions } from './OptionsBuilder.js';
|
|
2
|
+
import OrderedObjParser from './OrderedObjParser.js';
|
|
3
|
+
import prettify from './node2json.js';
|
|
4
|
+
import { validate } from "../validator.js";
|
|
5
|
+
import XmlNode from './xmlNode.js';
|
|
6
|
+
|
|
7
|
+
export default class XMLParser {
|
|
8
|
+
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.externalEntities = {};
|
|
11
|
+
this.options = buildOptions(options);
|
|
12
|
+
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse XML dats to JS object
|
|
16
|
+
* @param {string|Uint8Array} xmlData
|
|
17
|
+
* @param {boolean|Object} validationOption
|
|
18
|
+
*/
|
|
19
|
+
parse(xmlData, validationOption) {
|
|
20
|
+
if (typeof xmlData !== "string" && xmlData.toString) {
|
|
21
|
+
xmlData = xmlData.toString();
|
|
22
|
+
} else if (typeof xmlData !== "string") {
|
|
23
|
+
throw new Error("XML data is accepted in String or Bytes[] form.")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (validationOption) {
|
|
27
|
+
if (validationOption === true) validationOption = {}; //validate with default options
|
|
28
|
+
|
|
29
|
+
const result = validate(xmlData, validationOption);
|
|
30
|
+
if (result !== true) {
|
|
31
|
+
throw Error(`${result.err.msg}:${result.err.line}:${result.err.col}`)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const orderedObjParser = new OrderedObjParser(this.options);
|
|
35
|
+
orderedObjParser.addExternalEntities(this.externalEntities);
|
|
36
|
+
const orderedResult = orderedObjParser.parseXml(xmlData);
|
|
37
|
+
if (this.options.preserveOrder || orderedResult === undefined) return orderedResult;
|
|
38
|
+
else return prettify(orderedResult, this.options, orderedObjParser.matcher);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Add Entity which is not by default supported by this library
|
|
43
|
+
* @param {string} key
|
|
44
|
+
* @param {string} value
|
|
45
|
+
*/
|
|
46
|
+
addEntity(key, value) {
|
|
47
|
+
if (value.indexOf("&") !== -1) {
|
|
48
|
+
throw new Error("Entity value can't have '&'")
|
|
49
|
+
} else if (key.indexOf("&") !== -1 || key.indexOf(";") !== -1) {
|
|
50
|
+
throw new Error("An entity must be set without '&' and ';'. Eg. use '#xD' for '
'")
|
|
51
|
+
} else if (value === "&") {
|
|
52
|
+
throw new Error("An entity with value '&' is not permitted");
|
|
53
|
+
} else {
|
|
54
|
+
this.externalEntities[key] = value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Returns a Symbol that can be used to access the metadata
|
|
60
|
+
* property on a node.
|
|
61
|
+
*
|
|
62
|
+
* If Symbol is not available in the environment, an ordinary property is used
|
|
63
|
+
* and the name of the property is here returned.
|
|
64
|
+
*
|
|
65
|
+
* The XMLMetaData property is only present when `captureMetaData`
|
|
66
|
+
* is true in the options.
|
|
67
|
+
*/
|
|
68
|
+
static getMetaDataSymbol() {
|
|
69
|
+
return XmlNode.getMetaDataSymbol();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import XmlNode from './xmlNode.js';
|
|
4
|
+
import { Matcher } from 'path-expression-matcher';
|
|
5
|
+
|
|
6
|
+
const METADATA_SYMBOL = XmlNode.getMetaDataSymbol();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Helper function to strip attribute prefix from attribute map
|
|
10
|
+
* @param {object} attrs - Attributes with prefix (e.g., {"@_class": "code"})
|
|
11
|
+
* @param {string} prefix - Attribute prefix to remove (e.g., "@_")
|
|
12
|
+
* @returns {object} Attributes without prefix (e.g., {"class": "code"})
|
|
13
|
+
*/
|
|
14
|
+
function stripAttributePrefix(attrs, prefix) {
|
|
15
|
+
if (!attrs || typeof attrs !== 'object') return {};
|
|
16
|
+
if (!prefix) return attrs;
|
|
17
|
+
|
|
18
|
+
const rawAttrs = {};
|
|
19
|
+
for (const key in attrs) {
|
|
20
|
+
if (key.startsWith(prefix)) {
|
|
21
|
+
const rawName = key.substring(prefix.length);
|
|
22
|
+
rawAttrs[rawName] = attrs[key];
|
|
23
|
+
} else {
|
|
24
|
+
// Attribute without prefix (shouldn't normally happen, but be safe)
|
|
25
|
+
rawAttrs[key] = attrs[key];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return rawAttrs;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param {array} node
|
|
34
|
+
* @param {any} options
|
|
35
|
+
* @param {Matcher} matcher - Path matcher instance
|
|
36
|
+
* @returns
|
|
37
|
+
*/
|
|
38
|
+
export default function prettify(node, options, matcher) {
|
|
39
|
+
return compress(node, options, matcher);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
*
|
|
44
|
+
* @param {array} arr
|
|
45
|
+
* @param {object} options
|
|
46
|
+
* @param {Matcher} matcher - Path matcher instance
|
|
47
|
+
* @returns object
|
|
48
|
+
*/
|
|
49
|
+
function compress(arr, options, matcher) {
|
|
50
|
+
let text;
|
|
51
|
+
const compressedObj = {}; //This is intended to be a plain object
|
|
52
|
+
for (let i = 0; i < arr.length; i++) {
|
|
53
|
+
const tagObj = arr[i];
|
|
54
|
+
const property = propName(tagObj);
|
|
55
|
+
|
|
56
|
+
// Push current property to matcher WITH RAW ATTRIBUTES (no prefix)
|
|
57
|
+
if (property !== undefined && property !== options.textNodeName) {
|
|
58
|
+
const rawAttrs = stripAttributePrefix(
|
|
59
|
+
tagObj[":@"] || {},
|
|
60
|
+
options.attributeNamePrefix
|
|
61
|
+
);
|
|
62
|
+
matcher.push(property, rawAttrs);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (property === options.textNodeName) {
|
|
66
|
+
if (text === undefined) text = tagObj[property];
|
|
67
|
+
else text += "" + tagObj[property];
|
|
68
|
+
} else if (property === undefined) {
|
|
69
|
+
continue;
|
|
70
|
+
} else if (tagObj[property]) {
|
|
71
|
+
|
|
72
|
+
let val = compress(tagObj[property], options, matcher);
|
|
73
|
+
const isLeaf = isLeafTag(val, options);
|
|
74
|
+
|
|
75
|
+
if (tagObj[":@"]) {
|
|
76
|
+
assignAttributes(val, tagObj[":@"], matcher, options);
|
|
77
|
+
} else if (Object.keys(val).length === 1 && val[options.textNodeName] !== undefined && !options.alwaysCreateTextNode) {
|
|
78
|
+
val = val[options.textNodeName];
|
|
79
|
+
} else if (Object.keys(val).length === 0) {
|
|
80
|
+
if (options.alwaysCreateTextNode) val[options.textNodeName] = "";
|
|
81
|
+
else val = "";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (tagObj[METADATA_SYMBOL] !== undefined && typeof val === "object" && val !== null) {
|
|
85
|
+
val[METADATA_SYMBOL] = tagObj[METADATA_SYMBOL]; // copy over metadata
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if (compressedObj[property] !== undefined && Object.prototype.hasOwnProperty.call(compressedObj, property)) {
|
|
90
|
+
if (!Array.isArray(compressedObj[property])) {
|
|
91
|
+
compressedObj[property] = [compressedObj[property]];
|
|
92
|
+
}
|
|
93
|
+
compressedObj[property].push(val);
|
|
94
|
+
} else {
|
|
95
|
+
//TODO: if a node is not an array, then check if it should be an array
|
|
96
|
+
//also determine if it is a leaf node
|
|
97
|
+
|
|
98
|
+
// Pass jPath string or matcher based on options.jPath setting
|
|
99
|
+
const jPathOrMatcher = options.jPath ? matcher.toString() : matcher;
|
|
100
|
+
if (options.isArray(property, jPathOrMatcher, isLeaf)) {
|
|
101
|
+
compressedObj[property] = [val];
|
|
102
|
+
} else {
|
|
103
|
+
compressedObj[property] = val;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Pop property from matcher after processing
|
|
108
|
+
if (property !== undefined && property !== options.textNodeName) {
|
|
109
|
+
matcher.pop();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
}
|
|
114
|
+
// if(text && text.length > 0) compressedObj[options.textNodeName] = text;
|
|
115
|
+
if (typeof text === "string") {
|
|
116
|
+
if (text.length > 0) compressedObj[options.textNodeName] = text;
|
|
117
|
+
} else if (text !== undefined) compressedObj[options.textNodeName] = text;
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
return compressedObj;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function propName(obj) {
|
|
124
|
+
const keys = Object.keys(obj);
|
|
125
|
+
for (let i = 0; i < keys.length; i++) {
|
|
126
|
+
const key = keys[i];
|
|
127
|
+
if (key !== ":@") return key;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function assignAttributes(obj, attrMap, matcher, options) {
|
|
132
|
+
if (attrMap) {
|
|
133
|
+
const keys = Object.keys(attrMap);
|
|
134
|
+
const len = keys.length; //don't make it inline
|
|
135
|
+
for (let i = 0; i < len; i++) {
|
|
136
|
+
const atrrName = keys[i]; // This is the PREFIXED name (e.g., "@_class")
|
|
137
|
+
|
|
138
|
+
// Strip prefix for matcher path (for isArray callback)
|
|
139
|
+
const rawAttrName = atrrName.startsWith(options.attributeNamePrefix)
|
|
140
|
+
? atrrName.substring(options.attributeNamePrefix.length)
|
|
141
|
+
: atrrName;
|
|
142
|
+
|
|
143
|
+
// For attributes, we need to create a temporary path
|
|
144
|
+
// Pass jPath string or matcher based on options.jPath setting
|
|
145
|
+
const jPathOrMatcher = options.jPath
|
|
146
|
+
? matcher.toString() + "." + rawAttrName
|
|
147
|
+
: matcher;
|
|
148
|
+
|
|
149
|
+
if (options.isArray(atrrName, jPathOrMatcher, true, true)) {
|
|
150
|
+
obj[atrrName] = [attrMap[atrrName]];
|
|
151
|
+
} else {
|
|
152
|
+
obj[atrrName] = attrMap[atrrName];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function isLeafTag(obj, options) {
|
|
159
|
+
const { textNodeName } = options;
|
|
160
|
+
const propCount = Object.keys(obj).length;
|
|
161
|
+
|
|
162
|
+
if (propCount === 0) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (
|
|
167
|
+
propCount === 1 &&
|
|
168
|
+
(obj[textNodeName] || typeof obj[textNodeName] === "boolean" || obj[textNodeName] === 0)
|
|
169
|
+
) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
let METADATA_SYMBOL;
|
|
4
|
+
|
|
5
|
+
if (typeof Symbol !== "function") {
|
|
6
|
+
METADATA_SYMBOL = "@@xmlMetadata";
|
|
7
|
+
} else {
|
|
8
|
+
METADATA_SYMBOL = Symbol("XML Node Metadata");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default class XmlNode {
|
|
12
|
+
constructor(tagname) {
|
|
13
|
+
this.tagname = tagname;
|
|
14
|
+
this.child = []; //nested tags, text, cdata, comments in order
|
|
15
|
+
this[":@"] = Object.create(null); //attributes map
|
|
16
|
+
}
|
|
17
|
+
add(key, val) {
|
|
18
|
+
// this.child.push( {name : key, val: val, isCdata: isCdata });
|
|
19
|
+
if (key === "__proto__") key = "#__proto__";
|
|
20
|
+
this.child.push({ [key]: val });
|
|
21
|
+
}
|
|
22
|
+
addChild(node, startIndex) {
|
|
23
|
+
if (node.tagname === "__proto__") node.tagname = "#__proto__";
|
|
24
|
+
if (node[":@"] && Object.keys(node[":@"]).length > 0) {
|
|
25
|
+
this.child.push({ [node.tagname]: node.child, [":@"]: node[":@"] });
|
|
26
|
+
} else {
|
|
27
|
+
this.child.push({ [node.tagname]: node.child });
|
|
28
|
+
}
|
|
29
|
+
// if requested, add the startIndex
|
|
30
|
+
if (startIndex !== undefined) {
|
|
31
|
+
// Note: for now we just overwrite the metadata. If we had more complex metadata,
|
|
32
|
+
// we might need to do an object append here: metadata = { ...metadata, startIndex }
|
|
33
|
+
this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** symbol used for metadata */
|
|
37
|
+
static getMetaDataSymbol() {
|
|
38
|
+
return METADATA_SYMBOL;
|
|
39
|
+
}
|
|
40
|
+
}
|