fast-xml-parser 3.21.0 → 4.0.0-beta.3

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.
@@ -1,339 +0,0 @@
1
- 'use strict';
2
-
3
- const util = require('./util');
4
- const buildOptions = require('./util').buildOptions;
5
- const xmlNode = require('./xmlNode');
6
- const toNumber = require("strnum");
7
-
8
- const regx =
9
- '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
10
- .replace(/NAME/g, util.nameRegexp);
11
-
12
- //const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g");
13
- //const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g");
14
-
15
- //polyfill
16
- if (!Number.parseInt && window.parseInt) {
17
- Number.parseInt = window.parseInt;
18
- }
19
- if (!Number.parseFloat && window.parseFloat) {
20
- Number.parseFloat = window.parseFloat;
21
- }
22
-
23
- const defaultOptions = {
24
- attributeNamePrefix: '@_',
25
- attrNodeName: false,
26
- textNodeName: '#text',
27
- ignoreAttributes: true,
28
- ignoreNameSpace: false,
29
- allowBooleanAttributes: false, //a tag can have attributes without any value
30
- //ignoreRootElement : false,
31
- parseNodeValue: true,
32
- parseAttributeValue: false,
33
- arrayMode: false,
34
- trimValues: true, //Trim string values of tag and attributes
35
- cdataTagName: false,
36
- cdataPositionChar: '\\c',
37
- numParseOptions: {
38
- hex: true,
39
- leadingZeros: true
40
- },
41
- tagValueProcessor: function(a, tagName) {
42
- return a;
43
- },
44
- attrValueProcessor: function(a, attrName) {
45
- return a;
46
- },
47
- stopNodes: [],
48
- alwaysCreateTextNode: false
49
- //decodeStrict: false,
50
- };
51
-
52
- exports.defaultOptions = defaultOptions;
53
-
54
- const props = [
55
- 'attributeNamePrefix',
56
- 'attrNodeName',
57
- 'textNodeName',
58
- 'ignoreAttributes',
59
- 'ignoreNameSpace',
60
- 'allowBooleanAttributes',
61
- 'parseNodeValue',
62
- 'parseAttributeValue',
63
- 'arrayMode',
64
- 'trimValues',
65
- 'cdataTagName',
66
- 'cdataPositionChar',
67
- 'tagValueProcessor',
68
- 'attrValueProcessor',
69
- 'parseTrueNumberOnly',
70
- 'numParseOptions',
71
- 'stopNodes',
72
- 'alwaysCreateTextNode'
73
- ];
74
- exports.props = props;
75
-
76
- /**
77
- * Trim -> valueProcessor -> parse value
78
- * @param {string} tagName
79
- * @param {string} val
80
- * @param {object} options
81
- */
82
- function processTagValue(tagName, val, options) {
83
- if (val) {
84
- if (options.trimValues) {
85
- val = val.trim();
86
- }
87
- val = options.tagValueProcessor(val, tagName);
88
- val = parseValue(val, options.parseNodeValue, options.numParseOptions);
89
- }
90
-
91
- return val;
92
- }
93
-
94
- function resolveNameSpace(tagname, options) {
95
- if (options.ignoreNameSpace) {
96
- const tags = tagname.split(':');
97
- const prefix = tagname.charAt(0) === '/' ? '/' : '';
98
- if (tags[0] === 'xmlns') {
99
- return '';
100
- }
101
- if (tags.length === 2) {
102
- tagname = prefix + tags[1];
103
- }
104
- }
105
- return tagname;
106
- }
107
-
108
- function parseValue(val, shouldParse, options) {
109
- if (shouldParse && typeof val === 'string') {
110
- //console.log(options)
111
- const newval = val.trim();
112
- if(newval === 'true' ) return true;
113
- else if(newval === 'false' ) return false;
114
- else return toNumber(val, options);
115
- } else {
116
- if (util.isExist(val)) {
117
- return val;
118
- } else {
119
- return '';
120
- }
121
- }
122
- }
123
-
124
- //TODO: change regex to capture NS
125
- //const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
126
- const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])(.*?)\\3)?', 'g');
127
-
128
- function buildAttributesMap(attrStr, options) {
129
- if (!options.ignoreAttributes && typeof attrStr === 'string') {
130
- attrStr = attrStr.replace(/\r?\n/g, ' ');
131
- //attrStr = attrStr || attrStr.trim();
132
-
133
- const matches = util.getAllMatches(attrStr, attrsRegx);
134
- const len = matches.length; //don't make it inline
135
- const attrs = {};
136
- for (let i = 0; i < len; i++) {
137
- const attrName = resolveNameSpace(matches[i][1], options);
138
- if (attrName.length) {
139
- if (matches[i][4] !== undefined) {
140
- if (options.trimValues) {
141
- matches[i][4] = matches[i][4].trim();
142
- }
143
- matches[i][4] = options.attrValueProcessor(matches[i][4], attrName);
144
- attrs[options.attributeNamePrefix + attrName] = parseValue(
145
- matches[i][4],
146
- options.parseAttributeValue,
147
- options.numParseOptions
148
- );
149
- } else if (options.allowBooleanAttributes) {
150
- attrs[options.attributeNamePrefix + attrName] = true;
151
- }
152
- }
153
- }
154
- if (!Object.keys(attrs).length) {
155
- return;
156
- }
157
- if (options.attrNodeName) {
158
- const attrCollection = {};
159
- attrCollection[options.attrNodeName] = attrs;
160
- return attrCollection;
161
- }
162
- return attrs;
163
- }
164
- }
165
-
166
- const getTraversalObj = function(xmlData, options) {
167
- xmlData = xmlData.replace(/\r\n?/g, "\n");
168
- options = buildOptions(options, defaultOptions, props);
169
- const xmlObj = new xmlNode('!xml');
170
- let currentNode = xmlObj;
171
- let textData = "";
172
-
173
- //function match(xmlData){
174
- for(let i=0; i< xmlData.length; i++){
175
- const ch = xmlData[i];
176
- if(ch === '<'){
177
- if( xmlData[i+1] === '/') {//Closing Tag
178
- const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.")
179
- let tagName = xmlData.substring(i+2,closeIndex).trim();
180
-
181
- if(options.ignoreNameSpace){
182
- const colonIndex = tagName.indexOf(":");
183
- if(colonIndex !== -1){
184
- tagName = tagName.substr(colonIndex+1);
185
- }
186
- }
187
-
188
- /* if (currentNode.parent) {
189
- currentNode.parent.val = util.getValue(currentNode.parent.val) + '' + processTagValue2(tagName, textData , options);
190
- } */
191
- if(currentNode){
192
- if(currentNode.val){
193
- currentNode.val = util.getValue(currentNode.val) + '' + processTagValue(tagName, textData , options);
194
- }else{
195
- currentNode.val = processTagValue(tagName, textData , options);
196
- }
197
- }
198
-
199
- if (options.stopNodes.length && options.stopNodes.includes(currentNode.tagname)) {
200
- currentNode.child = []
201
- if (currentNode.attrsMap == undefined) { currentNode.attrsMap = {}}
202
- currentNode.val = xmlData.substr(currentNode.startIndex + 1, i - currentNode.startIndex - 1)
203
- }
204
- currentNode = currentNode.parent;
205
- textData = "";
206
- i = closeIndex;
207
- } else if( xmlData[i+1] === '?') {
208
- i = findClosingIndex(xmlData, "?>", i, "Pi Tag is not closed.")
209
- } else if(xmlData.substr(i + 1, 3) === '!--') {
210
- i = findClosingIndex(xmlData, "-->", i, "Comment is not closed.")
211
- } else if( xmlData.substr(i + 1, 2) === '!D') {
212
- const closeIndex = findClosingIndex(xmlData, ">", i, "DOCTYPE is not closed.")
213
- const tagExp = xmlData.substring(i, closeIndex);
214
- if(tagExp.indexOf("[") >= 0){
215
- i = xmlData.indexOf("]>", i) + 1;
216
- }else{
217
- i = closeIndex;
218
- }
219
- }else if(xmlData.substr(i + 1, 2) === '![') {
220
- const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2
221
- const tagExp = xmlData.substring(i + 9,closeIndex);
222
-
223
- //considerations
224
- //1. CDATA will always have parent node
225
- //2. A tag with CDATA is not a leaf node so it's value would be string type.
226
- if(textData){
227
- currentNode.val = util.getValue(currentNode.val) + '' + processTagValue(currentNode.tagname, textData , options);
228
- textData = "";
229
- }
230
-
231
- if (options.cdataTagName) {
232
- //add cdata node
233
- const childNode = new xmlNode(options.cdataTagName, currentNode, tagExp);
234
- currentNode.addChild(childNode);
235
- //for backtracking
236
- currentNode.val = util.getValue(currentNode.val) + options.cdataPositionChar;
237
- //add rest value to parent node
238
- if (tagExp) {
239
- childNode.val = tagExp;
240
- }
241
- } else {
242
- currentNode.val = (currentNode.val || '') + (tagExp || '');
243
- }
244
-
245
- i = closeIndex + 2;
246
- }else {//Opening tag
247
- const result = closingIndexForOpeningTag(xmlData, i+1)
248
- let tagExp = result.data;
249
- const closeIndex = result.index;
250
- const separatorIndex = tagExp.indexOf(" ");
251
- let tagName = tagExp;
252
- let shouldBuildAttributesMap = true;
253
- if(separatorIndex !== -1){
254
- tagName = tagExp.substr(0, separatorIndex).replace(/\s\s*$/, '');
255
- tagExp = tagExp.substr(separatorIndex + 1);
256
- }
257
-
258
- if(options.ignoreNameSpace){
259
- const colonIndex = tagName.indexOf(":");
260
- if(colonIndex !== -1){
261
- tagName = tagName.substr(colonIndex+1);
262
- shouldBuildAttributesMap = tagName !== result.data.substr(colonIndex + 1);
263
- }
264
- }
265
-
266
- //save text to parent node
267
- if (currentNode && textData) {
268
- if(currentNode.tagname !== '!xml'){
269
- currentNode.val = util.getValue(currentNode.val) + '' + processTagValue( currentNode.tagname, textData, options);
270
- }
271
- }
272
-
273
- if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){//selfClosing tag
274
-
275
- if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
276
- tagName = tagName.substr(0, tagName.length - 1);
277
- tagExp = tagName;
278
- }else{
279
- tagExp = tagExp.substr(0, tagExp.length - 1);
280
- }
281
-
282
- const childNode = new xmlNode(tagName, currentNode, '');
283
- if(tagName !== tagExp){
284
- childNode.attrsMap = buildAttributesMap(tagExp, options);
285
- }
286
- currentNode.addChild(childNode);
287
- }else{//opening tag
288
-
289
- const childNode = new xmlNode( tagName, currentNode );
290
- if (options.stopNodes.length && options.stopNodes.includes(childNode.tagname)) {
291
- childNode.startIndex=closeIndex;
292
- }
293
- if(tagName !== tagExp && shouldBuildAttributesMap){
294
- childNode.attrsMap = buildAttributesMap(tagExp, options);
295
- }
296
- currentNode.addChild(childNode);
297
- currentNode = childNode;
298
- }
299
- textData = "";
300
- i = closeIndex;
301
- }
302
- }else{
303
- textData += xmlData[i];
304
- }
305
- }
306
- return xmlObj;
307
- }
308
-
309
- function closingIndexForOpeningTag(data, i){
310
- let attrBoundary;
311
- let tagExp = "";
312
- for (let index = i; index < data.length; index++) {
313
- let ch = data[index];
314
- if (attrBoundary) {
315
- if (ch === attrBoundary) attrBoundary = "";//reset
316
- } else if (ch === '"' || ch === "'") {
317
- attrBoundary = ch;
318
- } else if (ch === '>') {
319
- return {
320
- data: tagExp,
321
- index: index
322
- }
323
- } else if (ch === '\t') {
324
- ch = " "
325
- }
326
- tagExp += ch;
327
- }
328
- }
329
-
330
- function findClosingIndex(xmlData, str, i, errMsg){
331
- const closingIndex = xmlData.indexOf(str, i);
332
- if(closingIndex === -1){
333
- throw new Error(errMsg)
334
- }else{
335
- return closingIndex + str.length - 1;
336
- }
337
- }
338
-
339
- exports.getTraversalObj = getTraversalObj;