node-opcua-xml2json 2.51.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.
Files changed (40) hide show
  1. package/.mocharc.yml +13 -0
  2. package/LICENSE +20 -0
  3. package/dist/source/definition_parser.d.ts +3 -0
  4. package/dist/source/definition_parser.js +84 -0
  5. package/dist/source/definition_parser.js.map +1 -0
  6. package/dist/source/extension_object_parser.d.ts +17 -0
  7. package/dist/source/extension_object_parser.js +234 -0
  8. package/dist/source/extension_object_parser.js.map +1 -0
  9. package/dist/source/fragment_cloner.d.ts +13 -0
  10. package/dist/source/fragment_cloner.js +41 -0
  11. package/dist/source/fragment_cloner.js.map +1 -0
  12. package/dist/source/fragment_cloner_parser.d.ts +8 -0
  13. package/dist/source/fragment_cloner_parser.js +17 -0
  14. package/dist/source/fragment_cloner_parser.js.map +1 -0
  15. package/dist/source/index.d.ts +6 -0
  16. package/dist/source/index.js +19 -0
  17. package/dist/source/index.js.map +1 -0
  18. package/dist/source/nodejs/xml2json_fs.d.ts +10 -0
  19. package/dist/source/nodejs/xml2json_fs.js +39 -0
  20. package/dist/source/nodejs/xml2json_fs.js.map +1 -0
  21. package/dist/source/xml2Json_pojo_tools.d.ts +19 -0
  22. package/dist/source/xml2Json_pojo_tools.js +89 -0
  23. package/dist/source/xml2Json_pojo_tools.js.map +1 -0
  24. package/dist/source/xml2json.d.ts +177 -0
  25. package/dist/source/xml2json.js +296 -0
  26. package/dist/source/xml2json.js.map +1 -0
  27. package/dist/source/xml2json_pojo.d.ts +7 -0
  28. package/dist/source/xml2json_pojo.js +29 -0
  29. package/dist/source/xml2json_pojo.js.map +1 -0
  30. package/package.json +40 -0
  31. package/pnpm-lock.yaml +77 -0
  32. package/source/definition_parser.ts +84 -0
  33. package/source/extension_object_parser.ts +276 -0
  34. package/source/fragment_cloner.ts +51 -0
  35. package/source/fragment_cloner_parser.ts +17 -0
  36. package/source/index.ts +6 -0
  37. package/source/nodejs/xml2json_fs.ts +43 -0
  38. package/source/xml2Json_pojo_tools.ts +98 -0
  39. package/source/xml2json.ts +394 -0
  40. package/source/xml2json_pojo.ts +27 -0
@@ -0,0 +1,394 @@
1
+ /**
2
+ * @module node-opcua-xml2json
3
+ * node -> see if https://github.com/isaacs/sax-js could be used instead
4
+ */
5
+
6
+ // tslint:disable:max-classes-per-file
7
+ // tslint:disable:no-var-requires
8
+ // tslint:disable:unified-signatures
9
+
10
+ import { assert } from "node-opcua-assert";
11
+ const LtxParser = require("ltx/lib/parsers/ltx.js");
12
+
13
+ export type SimpleCallback = (err?: Error) => void;
14
+ export type Callback<T> = (err?: Error | null, result?: T) => void;
15
+
16
+ declare interface LtxParser {
17
+ write(str: string): void;
18
+
19
+ end(): void;
20
+
21
+ on(eventName: "startElement", eventHandler: (name: string, attrs: XmlAttributes) => void): void;
22
+
23
+ on(eventName: "endElement", eventHandler: (name: string) => void): void;
24
+
25
+ on(eventName: "text", eventHandler: (name: string) => void): void;
26
+
27
+ on(eventName: "close", eventHandler: () => void): void;
28
+ }
29
+
30
+ export interface Parser {
31
+ [key: string]: ReaderState;
32
+ }
33
+
34
+ /**
35
+ * @static
36
+ * @private
37
+ * @method _coerceParser
38
+ * @param parser {map<ReaderState|options>}
39
+ * @return {map}
40
+ */
41
+ function _coerceParser(parser: ParserLike): Parser {
42
+ for (const name of Object.keys(parser)) {
43
+ if (parser[name] && !(parser[name] instanceof ReaderState)) {
44
+ // this is to prevent recursion
45
+ const tmp = parser[name];
46
+ delete parser[name];
47
+ parser[name] = new ReaderState(tmp);
48
+ }
49
+ }
50
+ return parser as Parser;
51
+ }
52
+
53
+ export interface XmlAttributes {
54
+ [key: string]: string;
55
+ }
56
+
57
+ export interface ReaderStateParser {
58
+ parser?: ParserLike;
59
+ init?: (this: IReaderState, name: string, attrs: XmlAttributes, parent: IReaderState, engine: Xml2Json) => void;
60
+ finish?: (this: IReaderState) => void;
61
+ startElement?: (this: IReaderState, name: string, attrs: XmlAttributes) => void;
62
+ endElement?: (this: IReaderState, name: string) => void;
63
+ }
64
+
65
+ export interface ParserLike {
66
+ [key: string]: ReaderStateParserLike;
67
+ }
68
+
69
+ export interface ReaderStateParserLike {
70
+ parser?: ParserLike;
71
+ init?: (this: any, name: string, attrs: XmlAttributes, parent: IReaderState, engine: Xml2Json) => void;
72
+ finish?: (this: any) => void;
73
+ startElement?: (this: any, name: string, attrs: XmlAttributes) => void;
74
+ endElement?: (this: any, name: string) => void;
75
+ }
76
+
77
+ export interface IReaderState {
78
+ _on_init(elementName: string, attrs: XmlAttributes, parent: IReaderState, level: number, engine: Xml2Json): void;
79
+
80
+ _on_finish(): void;
81
+
82
+ _on_startElement(level: number, elementName: string, attrs: XmlAttributes): void;
83
+
84
+ _on_endElement(level: number, elementName: string): void;
85
+
86
+ _on_endElement2(level: number, elementName: string): void;
87
+
88
+ _on_text(text: string): void;
89
+ }
90
+
91
+ export class ReaderStateBase {}
92
+ export interface ReaderStateBase extends IReaderState {}
93
+ /**
94
+ * @class ReaderState
95
+ * @private
96
+ * @param options
97
+ * @param [options.parser=null] {map<ReaderState|options}}
98
+ * @param [options.init|null]
99
+ * @param [options.finish]
100
+ * @param [options.startElement]
101
+ * @param [options.endElement]
102
+ */
103
+ export class ReaderState extends ReaderStateBase {
104
+ public _init?: (name: string, attrs: XmlAttributes, parent: IReaderState, engine: Xml2Json) => void;
105
+ public _finish?: () => void;
106
+ public _startElement?: (name: string, attrs: XmlAttributes) => void;
107
+ public _endElement?: (name: string) => void;
108
+
109
+ public parser: any;
110
+ public attrs?: XmlAttributes;
111
+ public chunks: any[] = [];
112
+ public text = "";
113
+ public name? = "";
114
+ public level = -1;
115
+ public currentLevel = -1;
116
+
117
+ public engine?: Xml2Json;
118
+
119
+ public parent?: IReaderState;
120
+ public root?: Xml2Json;
121
+ public data?: any;
122
+
123
+ constructor(options: ReaderStateParser | ReaderState) {
124
+ super();
125
+ // ensure options object has only expected properties
126
+ options.parser = options.parser || {};
127
+
128
+ if (!(options instanceof ReaderStateBase)) {
129
+ this._init = options.init;
130
+ this._finish = options.finish;
131
+ this._startElement = options.startElement;
132
+ this._endElement = options.endElement;
133
+ }
134
+
135
+ this.parser = _coerceParser(options.parser);
136
+ }
137
+
138
+ /**
139
+ * @method _on_init
140
+ * @param elementName - the name of the element
141
+ * @param attrs
142
+ * @param parent
143
+ * @param level
144
+ * @param engine
145
+ * @protected
146
+ */
147
+ public _on_init(elementName: string, attrs: XmlAttributes, parent: IReaderState, level: number, engine: Xml2Json): void {
148
+ this.name = elementName;
149
+ this.parent = parent;
150
+ this.engine = engine;
151
+ this.data = {};
152
+ this.level = level;
153
+ this.currentLevel = this.level;
154
+ this.attrs = attrs;
155
+ assert(this.attrs);
156
+ if (this._init) {
157
+ this._init(elementName, attrs, parent, engine);
158
+ }
159
+ }
160
+
161
+ public _on_finish(): void {
162
+ if (this._finish) {
163
+ this._finish();
164
+ }
165
+ }
166
+
167
+ /**
168
+ * @param level
169
+ * @param elementName - the name of the element
170
+ * @param attrs
171
+ * @protected
172
+ */
173
+ public _on_startElement(level: number, elementName: string, attrs: XmlAttributes): void {
174
+ this.currentLevel = level;
175
+ // console.log("wxxxx _on_startElement#" + this.name, elementName, this.currentLevel);
176
+
177
+ this.chunks = [];
178
+ this.text = "";
179
+
180
+ if (this._startElement) {
181
+ this._startElement(elementName, attrs);
182
+ }
183
+ if (this.engine && Object.prototype.hasOwnProperty.call(this.parser, elementName)) {
184
+ // console.log("promoting ", elementName, this.level);
185
+ this.engine._promote(this.parser[elementName], level, elementName, attrs);
186
+ }
187
+ }
188
+
189
+ public _on_endElement2(level: number, elementName: string): void {
190
+ if (this._endElement) {
191
+ this._endElement(elementName);
192
+ }
193
+ }
194
+
195
+ /**
196
+ * @method _on_endElement
197
+ * @protected
198
+ */
199
+ public _on_endElement(level: number, elementName: string): void {
200
+ // console.log("wxxxx _on_endElement#" + this.name, elementName, level, this.currentLevel);
201
+ assert(this.attrs);
202
+ this.chunks = this.chunks || [];
203
+
204
+ if (this.level > level) {
205
+ // we end a child element of this node
206
+ this._on_endElement2(level, elementName);
207
+ } else if (this.level === level) {
208
+ // we received the end event of this node
209
+ // we need to finish
210
+ this.text = this.chunks.join("");
211
+ this.chunks = [];
212
+ // this is the end
213
+ this._on_finish();
214
+ if (
215
+ this.parent &&
216
+ (this.parent as any).parser &&
217
+ Object.prototype.hasOwnProperty.call((this.parent as any).parser, elementName)
218
+ ) {
219
+ // console.log("xxx demoting#" + this.name, elementName, this.level);
220
+ this.engine!._demote(this, level, elementName);
221
+ }
222
+ }
223
+ }
224
+
225
+ /**
226
+ * @method _on_text
227
+ * @param text {String} the text found inside the element
228
+ * @protected
229
+ */
230
+ public _on_text(text: string): void {
231
+ this.chunks = this.chunks || [];
232
+ text = text.trim();
233
+ if (text.length === 0) {
234
+ return;
235
+ }
236
+ this.chunks.push(text);
237
+ }
238
+ }
239
+
240
+ const regexp = /(([^:]+):)?(.*)/;
241
+
242
+ function resolve_namespace(name: string) {
243
+ const m = name.match(regexp);
244
+ if (!m) {
245
+ throw new Error("Invalid match");
246
+ }
247
+ return {
248
+ ns: m[2],
249
+ tag: m[3]
250
+ };
251
+ }
252
+
253
+ /**
254
+ * @class Xml2Json
255
+ * @param options - the state machine as a ReaderState node.
256
+ * @param [options.parser=null] {ReaderState}
257
+ * @param [options.init|null]
258
+ * @param [options.finish]
259
+ * @param [options.startElement]
260
+ * @param [options.endElement]
261
+ * @constructor
262
+ *
263
+ * @example
264
+ * var parser = new Xml2Json({
265
+ * parser: {
266
+ * 'person': {
267
+ * init: function(name,attrs) {
268
+ * this.parent.root.obj = {};
269
+ * this.obj = this.parent.root.obj;
270
+ * this.obj['name'] = attrs['name'];
271
+ * },
272
+ * parser: {
273
+ * 'address': {
274
+ * finish: function(){
275
+ * this.parent.obj['address'] = this.text;
276
+ * }
277
+ * }
278
+ * }
279
+ * }
280
+ * }
281
+ * });
282
+ *
283
+ * var xml_string = "<employees>" +
284
+ * " <person name='John'>" +
285
+ * " <address>Paris</address>" +
286
+ * " </person>" +
287
+ * "</employees>";
288
+ *
289
+ * parser.parseString(xml_string, function() {
290
+ * parser.obj.should.eql({name: 'John',address: 'Paris'});
291
+ * done();
292
+ * });
293
+ */
294
+ export class Xml2Json {
295
+ public currentLevel = 0;
296
+ private state_stack: any[] = [];
297
+ private current_state: IReaderState | null = null;
298
+
299
+ constructor(options: ReaderStateParser) {
300
+ const state = options instanceof ReaderStateBase ? (options as ReaderState) : new ReaderState(options);
301
+ state.root = this;
302
+
303
+ this.state_stack = [];
304
+ this.current_state = null;
305
+ this._promote(state, 0);
306
+ }
307
+
308
+ /**
309
+ * @method parseString
310
+ * @async
311
+ */
312
+ public parseString(xml_text: string): Promise<any>;
313
+ public parseString(xml_text: string, callback: Callback<any> | SimpleCallback): void;
314
+ public parseString(xml_text: string, callback?: Callback<any> | SimpleCallback): any {
315
+ const parser = this._prepareParser(callback!);
316
+ parser.write(xml_text);
317
+ parser.end();
318
+ }
319
+ /**
320
+ * @param new_state
321
+ * @param name
322
+ * @param attr
323
+ * @private
324
+ * @internal
325
+ */
326
+ public _promote(new_state: IReaderState, level: number, name?: string, attr?: XmlAttributes): void {
327
+ attr = attr || {};
328
+ this.state_stack.push({
329
+ backup: {},
330
+ state: this.current_state
331
+ });
332
+
333
+ const parent = this.current_state;
334
+ this.current_state = new_state;
335
+ this.current_state._on_init(name || "???", attr, parent!, level, this);
336
+ }
337
+
338
+ /**
339
+ *
340
+ * @private
341
+ * @internal
342
+ */
343
+ public _demote(cur_state: IReaderState, level: number, elementName: string): void {
344
+ /// assert(this.current_state === cur_state);
345
+ const { state, backup } = this.state_stack.pop();
346
+ this.current_state = state;
347
+ if (this.current_state) {
348
+ this.current_state._on_endElement2(level, elementName);
349
+ }
350
+ }
351
+
352
+ protected _prepareParser(callback: Callback<any> | SimpleCallback): LtxParser {
353
+ assert(callback instanceof Function);
354
+ const parser = new LtxParser();
355
+ this.currentLevel = 0;
356
+ parser.on("startElement", (name: string, attrs: XmlAttributes) => {
357
+ const tag_ns = resolve_namespace(name);
358
+ this.currentLevel += 1;
359
+ if (this.current_state) {
360
+ this.current_state._on_startElement(this.currentLevel, tag_ns.tag, attrs);
361
+ }
362
+ });
363
+ parser.on("endElement", (name: string) => {
364
+ const tag_ns = resolve_namespace(name);
365
+ if (this.current_state) {
366
+ this.current_state._on_endElement(this.currentLevel, tag_ns.tag);
367
+ }
368
+ this.currentLevel -= 1;
369
+ if (this.currentLevel === 0) {
370
+ parser.emit("close");
371
+ }
372
+ });
373
+ parser.on("text", (text: string) => {
374
+ text = text.trim();
375
+ if (text.length === 0) {
376
+ return;
377
+ }
378
+ if (this.current_state) {
379
+ this.current_state._on_text(text);
380
+ }
381
+ });
382
+ parser.once("close", () => {
383
+ if (callback) {
384
+ (callback as any)(null, (this.current_state! as any)._pojo);
385
+ }
386
+ });
387
+ return parser;
388
+ }
389
+ }
390
+
391
+ // tslint:disable:no-var-requires
392
+ const thenify = require("thenify");
393
+ const opts = { multiArgs: false };
394
+ Xml2Json.prototype.parseString = thenify.withCallback(Xml2Json.prototype.parseString, opts);
@@ -0,0 +1,27 @@
1
+ import { IReaderState, ReaderState, ReaderStateParser, Xml2Json, XmlAttributes } from "./xml2json";
2
+ import { ReaderState2, withPojoLambda } from "./xml2Json_pojo_tools";
3
+
4
+
5
+ const json_extractor: ReaderState2 = new ReaderState2();
6
+ export const json_parser: ReaderStateParser = {
7
+ init(this: IReaderState, elementName: string, attrs: XmlAttributes, parent: IReaderState, engine: Xml2Json) {
8
+ json_extractor._on_init(elementName, attrs, parent, 0, engine);
9
+ },
10
+ finish(this: any) {
11
+ this.parent._pojo = json_extractor._pojo;
12
+ }
13
+ };
14
+
15
+ export function startPojo(pThis: ReaderState, elementName: string, attrs: XmlAttributes, withPojo: withPojoLambda) {
16
+ pThis.engine!._promote(json_extractor, pThis.engine!.currentLevel, elementName, attrs);
17
+ json_extractor._withPojo = (name: string, pojo: any) => {
18
+ withPojo(name, pojo);
19
+ pThis.engine!._demote(json_extractor, pThis.engine!.currentLevel, elementName);
20
+ };
21
+ }
22
+
23
+ export class Xml2JsonPojo extends Xml2Json {
24
+ constructor() {
25
+ super(json_extractor as ReaderStateParser);
26
+ }
27
+ }