-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathrss-parser.js
More file actions
159 lines (142 loc) · 3.75 KB
/
rss-parser.js
File metadata and controls
159 lines (142 loc) · 3.75 KB
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
function parseXML(string) {
/*
* Part 1
*
* Parse the xml into a DOM-like json
*
* Input: <tag attribute="value">text node<inline>text node</inline>text node</tag>
*
* Output:
* {
* "name": "root",
* "children": [
* {
* "name": "tag",
* "attrs": {
* "attribute": "value"
* },
* "innerText": "text node text node",
* "children": [
* {
* "name": "inline",
* "attrs": {},
* "innerText": "text node",
* "children": []
* }
* ]
* }
* ]
* }
*
*/
const parser = new XMLParser(string);
// Root of xml
const root = {
name: "root",
children: [],
};
// Store order for closing tags
const stack = [root];
// Create the new node and add it to the stack
parser.didStartElement = (name, attrs) => {
const node = {
name,
attrs,
innerText: "",
children: [],
};
stack.at(-1).children.push(node);
stack.push(node);
};
// Add the inner text to the node
parser.foundCharacters = (text) => {
const node = stack.at(-1);
node.innerText += node.innerText === "" ? text : " " + text;
};
// Remove element from the stack
parser.didEndElement = () => {
const removed = stack.pop();
if (stack.length === 0) {
throw new Error(`Unexpected closing tag: <${removed.name}/>`);
}
};
// Throw error on invalid input
parser.parseErrorOccurred = (message) => {
throw new Error(`A parse error occurred: ${message}`);
};
parser.parse();
if (stack.length !== 1) {
throw new Error(
`A parse error occurred, ensure all tags are closed and attributes are properly formatted: <${
stack.at(-1).name
}>`
);
}
/*
* Part 2
*
* Manipulate the AST-like json into a simpler json without attributes and other less important items. This will not collect both tag and text nodes if they are in the same tag.
*
* Input:
* {
* "name": "root",
* "children": [
* {
* "name": "tag",
* "attrs": {
* "attribute": "value"
* },
* "innerText": "text node text node",
* "children": [
* {
* "name": "inline",
* "attrs": {},
* "innerText": "text node",
* "children": []
* }
* ]
* }
* ]
* }
*
* Output:
* {
* "tag": {
* "inline": "text node"
* }
* }
*
* Notes: you can chose to only use part 1 if you need the attributes or text and tag nodes in the same tag. Replace the `return traverse(root)` below to `return root`
*/
// Return the new simple JSON
return traverse(root);
// Function to change each node
function traverse(node) {
// Store the new node
const newNode = {};
// Repeat with all children nodes
for (const child of node.children) {
// traverse the child
let newChild = traverse(child);
// If there are no children of the child than it should become a text value
if (child.children.length === 0) {
newChild = child.innerText;
}
// If the new node already has a key of the child's name it will become an array
if (newNode[child.name]) {
// If it is an array, push the new child
if (Array.isArray(newNode[child.name])) {
newNode[child.name].push(newChild);
} else {
// If it is not an array, change it into one
newNode[child.name] = [newNode[child.name], newChild];
}
} else {
// If it is not in the keys, set the child as a value in the new node
newNode[child.name] = newChild;
}
}
// Return the new node
return newNode;
}
}