-
Notifications
You must be signed in to change notification settings - Fork 2
/
base-parser.ts
112 lines (92 loc) · 2.84 KB
/
base-parser.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import * as sax from 'sax';
import { TreeNode } from "../xml/tree-node";
import { IS_NODE_CLI } from '../constants';
export class BaseParser {
protected rootNode: TreeNode;
protected currentNode: TreeNode;
protected stack: TreeNode[];
private mapUriNS: Record<string, string> = {
"http://www.vdv.de/ojp": "",
"http://www.siri.org.uk/siri": "siri",
};
constructor() {
this.rootNode = new TreeNode("root", null, {}, [], null);
this.currentNode = this.rootNode;
this.stack = [];
}
private resetNodes() {
this.rootNode = new TreeNode("root", null, {}, [], null);
this.currentNode = this.rootNode;
this.stack = [];
}
public parseXML(responseXMLText: string) {
if (IS_NODE_CLI) {
// 'sax' doesnt have a default export
// and "import * as sax from 'sax';"
// will fail for node CLI apps
import('sax').then((module) => {
const stream = module.default.createStream(true, { trim: true, xmlns: true });
this._parseXML(responseXMLText, stream);
});
} else {
const stream = sax.createStream(true, { trim: true, xmlns: true });
this._parseXML(responseXMLText, stream);
}
}
private _parseXML(responseXMLText: string, saxStream: sax.SAXStream) {
this.resetNodes();
saxStream.on('opentag', (node: sax.QualifiedTag) => {
this.onOpenTag(node);
});
saxStream.on('text', (text) => {
this.onText(text);
});
saxStream.on('closetag', (saxNodeName) => {
this.onSaxCloseTag(saxNodeName);
});
saxStream.on('error', (saxError) => {
this.onError(saxError);
});
saxStream.on('end', () => {
this.onEnd();
});
saxStream.write(responseXMLText);
saxStream.end();
}
private onOpenTag(node: sax.QualifiedTag) {
const nodeName = (() => {
const nodeParts = [];
const nodeNs = this.mapUriNS[node.uri] ?? '';
if (nodeNs !== '') {
nodeParts.push(nodeNs);
}
nodeParts.push(node.local);
return nodeParts.join(':');
})();
const newNode = new TreeNode(nodeName, this.currentNode.name, node.attributes, [], null);
this.currentNode.children.push(newNode);
this.stack.push(newNode);
this.currentNode = newNode;
}
private onText(text: string) {
this.currentNode.text = text;
}
private onSaxCloseTag(saxNodeName: string) {
// remove currentNode from stack
this.stack.pop();
// dont rely on callback saxNodeName because it might contain the wrong prefix
const nodeName = this.currentNode.name;
this.onCloseTag(nodeName);
// currentNode becomes latest item from the stack
this.currentNode = this.stack[this.stack.length - 1];
}
protected onCloseTag(nodeName: string) {
// override
}
protected onError(saxError: any) {
// override
}
protected onEnd(): void {
// override
}
}