fast-xml-parser 3.20.3 → 4.0.0-beta.2

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