@brickhouse-tech/xml2js 0.6.3 → 1.1.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.
- package/lib/bom.js +7 -12
- package/lib/builder.js +119 -98
- package/lib/defaults.js +74 -71
- package/lib/parser.js +356 -342
- package/lib/processors.js +23 -29
- package/lib/xml2js.js +20 -39
- package/package.json +22 -12
package/lib/parser.js
CHANGED
|
@@ -1,395 +1,409 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
hasProp = {}.hasOwnProperty;
|
|
1
|
+
import sax from 'sax';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { stripBOM } from './bom.js';
|
|
4
|
+
import * as processors from './processors.js';
|
|
5
|
+
import { setImmediate } from 'timers';
|
|
6
|
+
import { defaults } from './defaults.js';
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
// Underscore has a nice function for this, but we try to go without dependencies
|
|
9
|
+
function isEmpty(thing) {
|
|
10
|
+
return typeof thing === "object" && thing != null && Object.keys(thing).length === 0;
|
|
11
|
+
}
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
function processItem(processors, item, key) {
|
|
14
|
+
for (const process of processors) {
|
|
15
|
+
item = process(item, key);
|
|
16
|
+
}
|
|
17
|
+
return item;
|
|
18
|
+
}
|
|
12
19
|
|
|
13
|
-
|
|
20
|
+
function defineProperty(obj, key, value) {
|
|
21
|
+
// make sure the descriptor hasn't been prototype polluted
|
|
22
|
+
const descriptor = Object.create(null);
|
|
23
|
+
descriptor.value = value;
|
|
24
|
+
descriptor.writable = true;
|
|
25
|
+
descriptor.enumerable = true;
|
|
26
|
+
descriptor.configurable = true;
|
|
27
|
+
Object.defineProperty(obj, key, descriptor);
|
|
28
|
+
}
|
|
14
29
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
30
|
+
export class Parser extends EventEmitter {
|
|
31
|
+
constructor(opts) {
|
|
32
|
+
super();
|
|
33
|
+
// if this was called without 'new', create an instance with new and return
|
|
34
|
+
if (!(this instanceof Parser)) {
|
|
35
|
+
return new Parser(opts);
|
|
36
|
+
}
|
|
37
|
+
// copy this versions default options
|
|
38
|
+
this.options = {};
|
|
39
|
+
for (const key in defaults["0.2"]) {
|
|
40
|
+
if (Object.prototype.hasOwnProperty.call(defaults["0.2"], key)) {
|
|
41
|
+
this.options[key] = defaults["0.2"][key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// overwrite them with the specified options, if any
|
|
45
|
+
if (opts) {
|
|
46
|
+
for (const key in opts) {
|
|
47
|
+
if (Object.prototype.hasOwnProperty.call(opts, key)) {
|
|
48
|
+
this.options[key] = opts[key];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// define the key used for namespaces
|
|
53
|
+
if (this.options.xmlns) {
|
|
54
|
+
this.options.xmlnskey = this.options.attrkey + "ns";
|
|
55
|
+
}
|
|
56
|
+
if (this.options.normalizeTags) {
|
|
57
|
+
if (!this.options.tagNameProcessors) {
|
|
58
|
+
this.options.tagNameProcessors = [];
|
|
59
|
+
}
|
|
60
|
+
this.options.tagNameProcessors.unshift(processors.normalize);
|
|
61
|
+
}
|
|
18
62
|
|
|
19
|
-
|
|
63
|
+
this.reset();
|
|
64
|
+
}
|
|
20
65
|
|
|
21
|
-
|
|
22
|
-
|
|
66
|
+
processAsync = () => {
|
|
67
|
+
try {
|
|
68
|
+
if (this.remaining.length <= this.options.chunkSize) {
|
|
69
|
+
const chunk = this.remaining;
|
|
70
|
+
this.remaining = '';
|
|
71
|
+
this.saxParser = this.saxParser.write(chunk);
|
|
72
|
+
this.saxParser.close();
|
|
73
|
+
} else {
|
|
74
|
+
const chunk = this.remaining.substr(0, this.options.chunkSize);
|
|
75
|
+
this.remaining = this.remaining.substr(this.options.chunkSize, this.remaining.length);
|
|
76
|
+
this.saxParser = this.saxParser.write(chunk);
|
|
77
|
+
setImmediate(this.processAsync);
|
|
78
|
+
}
|
|
79
|
+
} catch (err) {
|
|
80
|
+
if (!this.saxParser.errThrown) {
|
|
81
|
+
this.saxParser.errThrown = true;
|
|
82
|
+
this.emit('error', err);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
23
85
|
};
|
|
24
86
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
87
|
+
assignOrPush = (obj, key, newValue) => {
|
|
88
|
+
if (!(key in obj)) {
|
|
89
|
+
if (!this.options.explicitArray) {
|
|
90
|
+
defineProperty(obj, key, newValue);
|
|
91
|
+
} else {
|
|
92
|
+
defineProperty(obj, key, [newValue]);
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
if (!(obj[key] instanceof Array)) {
|
|
96
|
+
defineProperty(obj, key, [obj[key]]);
|
|
97
|
+
}
|
|
98
|
+
obj[key].push(newValue);
|
|
30
99
|
}
|
|
31
|
-
return item;
|
|
32
100
|
};
|
|
33
101
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
102
|
+
reset = () => {
|
|
103
|
+
// remove all previous listeners for events, to prevent event listener
|
|
104
|
+
// accumulation
|
|
105
|
+
this.removeAllListeners();
|
|
106
|
+
// make the SAX parser. tried trim and normalize, but they are not
|
|
107
|
+
// very helpful
|
|
108
|
+
this.saxParser = sax.parser(this.options.strict, {
|
|
109
|
+
trim: false,
|
|
110
|
+
normalize: false,
|
|
111
|
+
xmlns: this.options.xmlns
|
|
112
|
+
});
|
|
43
113
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this.processAsync = bind(this.processAsync, this);
|
|
53
|
-
var key, ref, value;
|
|
54
|
-
if (!(this instanceof exports.Parser)) {
|
|
55
|
-
return new exports.Parser(opts);
|
|
114
|
+
// emit one error event if the sax parser fails. this is mostly a hack, but
|
|
115
|
+
// the sax parser isn't state of the art either.
|
|
116
|
+
this.saxParser.errThrown = false;
|
|
117
|
+
this.saxParser.onerror = (error) => {
|
|
118
|
+
this.saxParser.resume();
|
|
119
|
+
if (!this.saxParser.errThrown) {
|
|
120
|
+
this.saxParser.errThrown = true;
|
|
121
|
+
this.emit("error", error);
|
|
56
122
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
this.
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
this.saxParser.onend = () => {
|
|
126
|
+
if (!this.saxParser.ended) {
|
|
127
|
+
this.saxParser.ended = true;
|
|
128
|
+
this.emit("end", this.resultObject);
|
|
63
129
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// another hack to avoid throwing exceptions when the parsing has ended
|
|
133
|
+
// but the user-supplied callback throws an error
|
|
134
|
+
this.saxParser.ended = false;
|
|
135
|
+
|
|
136
|
+
// always use the '#' key, even if there are no subkeys
|
|
137
|
+
// setting this property by and is deprecated, yet still supported.
|
|
138
|
+
// better pass it as explicitCharkey option to the constructor
|
|
139
|
+
this.EXPLICIT_CHARKEY = this.options.explicitCharkey;
|
|
140
|
+
this.resultObject = null;
|
|
141
|
+
const stack = [];
|
|
142
|
+
// aliases, so we don't have to type so much
|
|
143
|
+
const attrkey = this.options.attrkey;
|
|
144
|
+
const charkey = this.options.charkey;
|
|
145
|
+
|
|
146
|
+
this.saxParser.onopentag = (node) => {
|
|
147
|
+
const obj = {};
|
|
148
|
+
obj[charkey] = "";
|
|
149
|
+
if (!this.options.ignoreAttrs) {
|
|
150
|
+
for (const key in node.attributes) {
|
|
151
|
+
if (Object.prototype.hasOwnProperty.call(node.attributes, key)) {
|
|
152
|
+
if (!(attrkey in obj) && !this.options.mergeAttrs) {
|
|
153
|
+
obj[attrkey] = {};
|
|
154
|
+
}
|
|
155
|
+
const newValue = this.options.attrValueProcessors
|
|
156
|
+
? processItem(this.options.attrValueProcessors, node.attributes[key], key)
|
|
157
|
+
: node.attributes[key];
|
|
158
|
+
const processedKey = this.options.attrNameProcessors
|
|
159
|
+
? processItem(this.options.attrNameProcessors, key)
|
|
160
|
+
: key;
|
|
161
|
+
if (this.options.mergeAttrs) {
|
|
162
|
+
this.assignOrPush(obj, processedKey, newValue);
|
|
163
|
+
} else {
|
|
164
|
+
defineProperty(obj[attrkey], processedKey, newValue);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
68
168
|
}
|
|
169
|
+
|
|
170
|
+
// need a place to store the node name
|
|
171
|
+
obj["#name"] = this.options.tagNameProcessors
|
|
172
|
+
? processItem(this.options.tagNameProcessors, node.name)
|
|
173
|
+
: node.name;
|
|
69
174
|
if (this.options.xmlns) {
|
|
70
|
-
this.options.xmlnskey =
|
|
175
|
+
obj[this.options.xmlnskey] = { uri: node.uri, local: node.local };
|
|
71
176
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
177
|
+
stack.push(obj);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
this.saxParser.onclosetag = () => {
|
|
181
|
+
let obj = stack.pop();
|
|
182
|
+
const nodeName = obj["#name"];
|
|
183
|
+
if (!this.options.explicitChildren || !this.options.preserveChildrenOrder) {
|
|
184
|
+
delete obj["#name"];
|
|
77
185
|
}
|
|
78
|
-
this.reset();
|
|
79
|
-
}
|
|
80
186
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
187
|
+
let cdata;
|
|
188
|
+
if (obj.cdata === true) {
|
|
189
|
+
cdata = obj.cdata;
|
|
190
|
+
delete obj.cdata;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const s = stack[stack.length - 1];
|
|
194
|
+
let emptyStr;
|
|
195
|
+
// remove the '#' key altogether if it's blank
|
|
196
|
+
if (obj[charkey].match(/^\s*$/) && !cdata) {
|
|
197
|
+
emptyStr = obj[charkey];
|
|
198
|
+
delete obj[charkey];
|
|
199
|
+
} else {
|
|
200
|
+
if (this.options.trim) {
|
|
201
|
+
obj[charkey] = obj[charkey].trim();
|
|
202
|
+
}
|
|
203
|
+
if (this.options.normalize) {
|
|
204
|
+
obj[charkey] = obj[charkey].replace(/\s{2,}/g, " ").trim();
|
|
94
205
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
206
|
+
obj[charkey] = this.options.valueProcessors
|
|
207
|
+
? processItem(this.options.valueProcessors, obj[charkey], nodeName)
|
|
208
|
+
: obj[charkey];
|
|
209
|
+
// also do away with '#' key altogether, if there's no subkeys
|
|
210
|
+
// unless EXPLICIT_CHARKEY is set
|
|
211
|
+
if (Object.keys(obj).length === 1 && charkey in obj && !this.EXPLICIT_CHARKEY) {
|
|
212
|
+
obj = obj[charkey];
|
|
100
213
|
}
|
|
101
214
|
}
|
|
102
|
-
};
|
|
103
215
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return defineProperty(obj, key, newValue);
|
|
216
|
+
if (isEmpty(obj)) {
|
|
217
|
+
if (typeof this.options.emptyTag === 'function') {
|
|
218
|
+
obj = this.options.emptyTag();
|
|
108
219
|
} else {
|
|
109
|
-
|
|
220
|
+
obj = this.options.emptyTag !== '' ? this.options.emptyTag : emptyStr;
|
|
110
221
|
}
|
|
111
|
-
} else {
|
|
112
|
-
if (!(obj[key] instanceof Array)) {
|
|
113
|
-
defineProperty(obj, key, [obj[key]]);
|
|
114
|
-
}
|
|
115
|
-
return obj[key].push(newValue);
|
|
116
222
|
}
|
|
117
|
-
};
|
|
118
223
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
this.saxParser.onerror = (function(_this) {
|
|
129
|
-
return function(error) {
|
|
130
|
-
_this.saxParser.resume();
|
|
131
|
-
if (!_this.saxParser.errThrown) {
|
|
132
|
-
_this.saxParser.errThrown = true;
|
|
133
|
-
return _this.emit("error", error);
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
})(this);
|
|
137
|
-
this.saxParser.onend = (function(_this) {
|
|
138
|
-
return function() {
|
|
139
|
-
if (!_this.saxParser.ended) {
|
|
140
|
-
_this.saxParser.ended = true;
|
|
141
|
-
return _this.emit("end", _this.resultObject);
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
})(this);
|
|
145
|
-
this.saxParser.ended = false;
|
|
146
|
-
this.EXPLICIT_CHARKEY = this.options.explicitCharkey;
|
|
147
|
-
this.resultObject = null;
|
|
148
|
-
stack = [];
|
|
149
|
-
attrkey = this.options.attrkey;
|
|
150
|
-
charkey = this.options.charkey;
|
|
151
|
-
this.saxParser.onopentag = (function(_this) {
|
|
152
|
-
return function(node) {
|
|
153
|
-
var key, newValue, obj, processedKey, ref;
|
|
154
|
-
obj = {};
|
|
155
|
-
obj[charkey] = "";
|
|
156
|
-
if (!_this.options.ignoreAttrs) {
|
|
157
|
-
ref = node.attributes;
|
|
158
|
-
for (key in ref) {
|
|
159
|
-
if (!hasProp.call(ref, key)) continue;
|
|
160
|
-
if (!(attrkey in obj) && !_this.options.mergeAttrs) {
|
|
161
|
-
obj[attrkey] = {};
|
|
162
|
-
}
|
|
163
|
-
newValue = _this.options.attrValueProcessors ? processItem(_this.options.attrValueProcessors, node.attributes[key], key) : node.attributes[key];
|
|
164
|
-
processedKey = _this.options.attrNameProcessors ? processItem(_this.options.attrNameProcessors, key) : key;
|
|
165
|
-
if (_this.options.mergeAttrs) {
|
|
166
|
-
_this.assignOrPush(obj, processedKey, newValue);
|
|
167
|
-
} else {
|
|
168
|
-
defineProperty(obj[attrkey], processedKey, newValue);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
obj["#name"] = _this.options.tagNameProcessors ? processItem(_this.options.tagNameProcessors, node.name) : node.name;
|
|
173
|
-
if (_this.options.xmlns) {
|
|
174
|
-
obj[_this.options.xmlnskey] = {
|
|
175
|
-
uri: node.uri,
|
|
176
|
-
local: node.local
|
|
177
|
-
};
|
|
224
|
+
if (this.options.validator != null) {
|
|
225
|
+
const xpath = "/" + stack.map(node => node["#name"]).concat(nodeName).join("/");
|
|
226
|
+
// Wrap try/catch with an inner function to allow V8 to optimise the containing function
|
|
227
|
+
// See https://github.com/Leonidas-from-XIV/node-xml2js/pull/369
|
|
228
|
+
(() => {
|
|
229
|
+
try {
|
|
230
|
+
obj = this.options.validator(xpath, s && s[nodeName], obj);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
this.emit("error", err);
|
|
178
233
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if (obj.cdata === true) {
|
|
191
|
-
cdata = obj.cdata;
|
|
192
|
-
delete obj.cdata;
|
|
193
|
-
}
|
|
194
|
-
s = stack[stack.length - 1];
|
|
195
|
-
if (obj[charkey].match(/^\s*$/) && !cdata) {
|
|
196
|
-
emptyStr = obj[charkey];
|
|
197
|
-
delete obj[charkey];
|
|
198
|
-
} else {
|
|
199
|
-
if (_this.options.trim) {
|
|
200
|
-
obj[charkey] = obj[charkey].trim();
|
|
201
|
-
}
|
|
202
|
-
if (_this.options.normalize) {
|
|
203
|
-
obj[charkey] = obj[charkey].replace(/\s{2,}/g, " ").trim();
|
|
204
|
-
}
|
|
205
|
-
obj[charkey] = _this.options.valueProcessors ? processItem(_this.options.valueProcessors, obj[charkey], nodeName) : obj[charkey];
|
|
206
|
-
if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
|
|
207
|
-
obj = obj[charkey];
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
if (isEmpty(obj)) {
|
|
211
|
-
if (typeof _this.options.emptyTag === 'function') {
|
|
212
|
-
obj = _this.options.emptyTag();
|
|
213
|
-
} else {
|
|
214
|
-
obj = _this.options.emptyTag !== '' ? _this.options.emptyTag : emptyStr;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (_this.options.validator != null) {
|
|
218
|
-
xpath = "/" + ((function() {
|
|
219
|
-
var i, len, results;
|
|
220
|
-
results = [];
|
|
221
|
-
for (i = 0, len = stack.length; i < len; i++) {
|
|
222
|
-
node = stack[i];
|
|
223
|
-
results.push(node["#name"]);
|
|
224
|
-
}
|
|
225
|
-
return results;
|
|
226
|
-
})()).concat(nodeName).join("/");
|
|
227
|
-
(function() {
|
|
228
|
-
var err;
|
|
229
|
-
try {
|
|
230
|
-
return obj = _this.options.validator(xpath, s && s[nodeName], obj);
|
|
231
|
-
} catch (error1) {
|
|
232
|
-
err = error1;
|
|
233
|
-
return _this.emit("error", err);
|
|
234
|
-
}
|
|
235
|
-
})();
|
|
234
|
+
})();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// put children into <childkey> property and unfold chars if necessary
|
|
238
|
+
if (this.options.explicitChildren && !this.options.mergeAttrs && typeof obj === 'object') {
|
|
239
|
+
if (!this.options.preserveChildrenOrder) {
|
|
240
|
+
let node = {};
|
|
241
|
+
// separate attributes
|
|
242
|
+
if (this.options.attrkey in obj) {
|
|
243
|
+
node[this.options.attrkey] = obj[this.options.attrkey];
|
|
244
|
+
delete obj[this.options.attrkey];
|
|
236
245
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
node[_this.options.attrkey] = obj[_this.options.attrkey];
|
|
242
|
-
delete obj[_this.options.attrkey];
|
|
243
|
-
}
|
|
244
|
-
if (!_this.options.charsAsChildren && _this.options.charkey in obj) {
|
|
245
|
-
node[_this.options.charkey] = obj[_this.options.charkey];
|
|
246
|
-
delete obj[_this.options.charkey];
|
|
247
|
-
}
|
|
248
|
-
if (Object.getOwnPropertyNames(obj).length > 0) {
|
|
249
|
-
node[_this.options.childkey] = obj;
|
|
250
|
-
}
|
|
251
|
-
obj = node;
|
|
252
|
-
} else if (s) {
|
|
253
|
-
s[_this.options.childkey] = s[_this.options.childkey] || [];
|
|
254
|
-
objClone = {};
|
|
255
|
-
for (key in obj) {
|
|
256
|
-
if (!hasProp.call(obj, key)) continue;
|
|
257
|
-
defineProperty(objClone, key, obj[key]);
|
|
258
|
-
}
|
|
259
|
-
s[_this.options.childkey].push(objClone);
|
|
260
|
-
delete obj["#name"];
|
|
261
|
-
if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
|
|
262
|
-
obj = obj[charkey];
|
|
263
|
-
}
|
|
264
|
-
}
|
|
246
|
+
// separate char data
|
|
247
|
+
if (!this.options.charsAsChildren && this.options.charkey in obj) {
|
|
248
|
+
node[this.options.charkey] = obj[this.options.charkey];
|
|
249
|
+
delete obj[this.options.charkey];
|
|
265
250
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (_this.options.explicitRoot) {
|
|
270
|
-
old = obj;
|
|
271
|
-
obj = {};
|
|
272
|
-
defineProperty(obj, nodeName, old);
|
|
273
|
-
}
|
|
274
|
-
_this.resultObject = obj;
|
|
275
|
-
_this.saxParser.ended = true;
|
|
276
|
-
return _this.emit("end", _this.resultObject);
|
|
251
|
+
|
|
252
|
+
if (Object.getOwnPropertyNames(obj).length > 0) {
|
|
253
|
+
node[this.options.childkey] = obj;
|
|
277
254
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (
|
|
287
|
-
|
|
288
|
-
charChild = {
|
|
289
|
-
'#name': '__text__'
|
|
290
|
-
};
|
|
291
|
-
charChild[charkey] = text;
|
|
292
|
-
if (_this.options.normalize) {
|
|
293
|
-
charChild[charkey] = charChild[charkey].replace(/\s{2,}/g, " ").trim();
|
|
294
|
-
}
|
|
295
|
-
s[_this.options.childkey].push(charChild);
|
|
255
|
+
|
|
256
|
+
obj = node;
|
|
257
|
+
} else if (s) {
|
|
258
|
+
// append current node onto parent's <childKey> array
|
|
259
|
+
s[this.options.childkey] = s[this.options.childkey] || [];
|
|
260
|
+
// push a clone so that the node in the children array can receive the #name property while the original obj can do without it
|
|
261
|
+
const objClone = {};
|
|
262
|
+
for (const key in obj) {
|
|
263
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
264
|
+
defineProperty(objClone, key, obj[key]);
|
|
296
265
|
}
|
|
297
|
-
return s;
|
|
298
266
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
var s;
|
|
305
|
-
s = ontext(text);
|
|
306
|
-
if (s) {
|
|
307
|
-
return s.cdata = true;
|
|
267
|
+
s[this.options.childkey].push(objClone);
|
|
268
|
+
delete obj["#name"];
|
|
269
|
+
// re-check whether we can collapse the node now to just the charkey value
|
|
270
|
+
if (Object.keys(obj).length === 1 && charkey in obj && !this.EXPLICIT_CHARKEY) {
|
|
271
|
+
obj = obj[charkey];
|
|
308
272
|
}
|
|
309
|
-
};
|
|
310
|
-
})(this);
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
Parser.prototype.parseString = function(str, cb) {
|
|
314
|
-
var err;
|
|
315
|
-
if ((cb != null) && typeof cb === "function") {
|
|
316
|
-
this.on("end", function(result) {
|
|
317
|
-
this.reset();
|
|
318
|
-
return cb(null, result);
|
|
319
|
-
});
|
|
320
|
-
this.on("error", function(err) {
|
|
321
|
-
this.reset();
|
|
322
|
-
return cb(err);
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
try {
|
|
326
|
-
str = str.toString();
|
|
327
|
-
if (str.trim() === '') {
|
|
328
|
-
this.emit("end", null);
|
|
329
|
-
return true;
|
|
330
|
-
}
|
|
331
|
-
str = bom.stripBOM(str);
|
|
332
|
-
if (this.options.async) {
|
|
333
|
-
this.remaining = str;
|
|
334
|
-
setImmediate(this.processAsync);
|
|
335
|
-
return this.saxParser;
|
|
336
273
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// check whether we closed all the open tags
|
|
277
|
+
if (stack.length > 0) {
|
|
278
|
+
this.assignOrPush(s, nodeName, obj);
|
|
279
|
+
} else {
|
|
280
|
+
// if explicitRoot was specified, wrap stuff in the root tag name
|
|
281
|
+
if (this.options.explicitRoot) {
|
|
282
|
+
// avoid circular references
|
|
283
|
+
const old = obj;
|
|
284
|
+
obj = {};
|
|
285
|
+
defineProperty(obj, nodeName, old);
|
|
345
286
|
}
|
|
287
|
+
|
|
288
|
+
this.resultObject = obj;
|
|
289
|
+
// parsing has ended, mark that so we won't throw exceptions from
|
|
290
|
+
// here anymore
|
|
291
|
+
this.saxParser.ended = true;
|
|
292
|
+
this.emit("end", this.resultObject);
|
|
346
293
|
}
|
|
347
294
|
};
|
|
348
295
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
296
|
+
const ontext = (text) => {
|
|
297
|
+
const s = stack[stack.length - 1];
|
|
298
|
+
if (s) {
|
|
299
|
+
s[charkey] += text;
|
|
300
|
+
|
|
301
|
+
if (this.options.explicitChildren && this.options.preserveChildrenOrder &&
|
|
302
|
+
this.options.charsAsChildren &&
|
|
303
|
+
(this.options.includeWhiteChars || text.replace(/\\n/g, '').trim() !== '')) {
|
|
304
|
+
s[this.options.childkey] = s[this.options.childkey] || [];
|
|
305
|
+
const charChild = {
|
|
306
|
+
'#name': '__text__'
|
|
307
|
+
};
|
|
308
|
+
charChild[charkey] = text;
|
|
309
|
+
if (this.options.normalize) {
|
|
310
|
+
charChild[charkey] = charChild[charkey].replace(/\s{2,}/g, " ").trim();
|
|
311
|
+
}
|
|
312
|
+
s[this.options.childkey].push(charChild);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return s;
|
|
316
|
+
}
|
|
361
317
|
};
|
|
362
318
|
|
|
363
|
-
|
|
319
|
+
this.saxParser.ontext = ontext;
|
|
320
|
+
this.saxParser.oncdata = (text) => {
|
|
321
|
+
const s = ontext(text);
|
|
322
|
+
if (s) {
|
|
323
|
+
s.cdata = true;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
};
|
|
364
327
|
|
|
365
|
-
|
|
328
|
+
parseString = (str, cb) => {
|
|
329
|
+
if (cb != null && typeof cb === "function") {
|
|
330
|
+
this.on("end", (result) => {
|
|
331
|
+
this.reset();
|
|
332
|
+
cb(null, result);
|
|
333
|
+
});
|
|
334
|
+
this.on("error", (err) => {
|
|
335
|
+
this.reset();
|
|
336
|
+
cb(err);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
366
339
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
340
|
+
try {
|
|
341
|
+
str = str.toString();
|
|
342
|
+
if (str.trim() === '') {
|
|
343
|
+
this.emit("end", null);
|
|
344
|
+
return true;
|
|
372
345
|
}
|
|
373
|
-
|
|
374
|
-
|
|
346
|
+
|
|
347
|
+
str = stripBOM(str);
|
|
348
|
+
if (this.options.async) {
|
|
349
|
+
this.remaining = str;
|
|
350
|
+
setImmediate(this.processAsync);
|
|
351
|
+
return this.saxParser;
|
|
375
352
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
353
|
+
this.saxParser.write(str).close();
|
|
354
|
+
} catch (err) {
|
|
355
|
+
if (!this.saxParser.errThrown && !this.saxParser.ended) {
|
|
356
|
+
this.emit('error', err);
|
|
357
|
+
this.saxParser.errThrown = true;
|
|
358
|
+
} else if (this.saxParser.ended) {
|
|
359
|
+
throw err;
|
|
379
360
|
}
|
|
380
|
-
options = {};
|
|
381
361
|
}
|
|
382
|
-
parser = new exports.Parser(options);
|
|
383
|
-
return parser.parseString(str, cb);
|
|
384
362
|
};
|
|
385
363
|
|
|
386
|
-
|
|
387
|
-
|
|
364
|
+
parseStringPromise = (str) => {
|
|
365
|
+
return new Promise((resolve, reject) => {
|
|
366
|
+
this.parseString(str, (err, value) => {
|
|
367
|
+
if (err) {
|
|
368
|
+
reject(err);
|
|
369
|
+
} else {
|
|
370
|
+
resolve(value);
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export function parseString(str, a, b) {
|
|
378
|
+
let cb, options;
|
|
379
|
+
// let's determine what we got as arguments
|
|
380
|
+
if (b != null) {
|
|
381
|
+
if (typeof b === 'function') {
|
|
382
|
+
cb = b;
|
|
383
|
+
}
|
|
388
384
|
if (typeof a === 'object') {
|
|
389
385
|
options = a;
|
|
390
386
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
387
|
+
} else {
|
|
388
|
+
// well, b is not set, so a has to be a callback
|
|
389
|
+
if (typeof a === 'function') {
|
|
390
|
+
cb = a;
|
|
391
|
+
}
|
|
392
|
+
// and options should be empty - default
|
|
393
|
+
options = {};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// the rest is super-easy
|
|
397
|
+
const parser = new Parser(options);
|
|
398
|
+
return parser.parseString(str, cb);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function parseStringPromise(str, a) {
|
|
402
|
+
let options = {};
|
|
403
|
+
if (typeof a === 'object') {
|
|
404
|
+
options = a;
|
|
405
|
+
}
|
|
394
406
|
|
|
395
|
-
|
|
407
|
+
const parser = new Parser(options);
|
|
408
|
+
return parser.parseStringPromise(str);
|
|
409
|
+
}
|