├── .gitignore
├── spec
├── support
│ └── jasmine.json
├── mockXML
│ └── index.js
└── traverse-spec.js
├── bin
└── tojson.js
├── lib
├── validator.js
├── cleanXML.js
├── xmlToJsonStream.js
└── xmlToJson.js
├── package.json
├── index.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | playground.js
3 | test.js
4 | test.xml
5 | .DS_Store
6 | .vscode/
--------------------------------------------------------------------------------
/spec/support/jasmine.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "spec",
3 | "spec_files": [
4 | "**/*[sS]pec.js"
5 | ],
6 | "helpers": [
7 | "helpers/**/*.js"
8 | ],
9 | "stopSpecOnExpectationFailure": false,
10 | "random": true
11 | }
12 |
--------------------------------------------------------------------------------
/bin/tojson.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const xmlToJson = require('../index')
4 | const [,, ...args] = process.argv;
5 |
6 | const attributeMode = args.includes("-no-attr") ? false : true;
7 |
8 | const parser = xmlToJson({attributeMode})
9 |
10 | const stream = parser.createStream();
11 |
12 | process.stdin.pipe(stream).pipe(process.stdout)
--------------------------------------------------------------------------------
/lib/validator.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | module.exports = function(tag) {
4 | if((tag.charAt(0) === '<' && tag.charAt(1) === '?') && (tag.charAt(tag.length-1) === '>' && tag.charAt(tag.length-2) === '?')) {
5 | return true;
6 | }
7 |
8 | if(tag.charAt(0) === '<' && (tag.charAt(tag.length-2)+tag.charAt(tag.length-1) === '/>' || tag.charAt(tag.length-1) === '>')) {
9 | return true;
10 | }
11 |
12 | return false;
13 | }
--------------------------------------------------------------------------------
/lib/cleanXML.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function cleanXML(xml) {
3 | return xml.replace(/>\s*<') //remove white spaces between elements
4 | .replace(/<\?xml.*\?>/g, '') //remove the root element
5 | .replace(//g,'') //remove comments
6 | .replace(/>\s*/g, '>') // remove any white spaces at the end of the xml string if any
7 | .replace(/\s*
2 | Alex
3 | `;
4 |
5 | const TEST2 = `Alex
6 | Jon`
7 |
8 | const TEST3 = `
9 | Alex
10 | 25
11 | `
12 |
13 | const TEST4 = `
14 | `
15 |
16 | const TEST5 = `
17 |
18 | Software Dev
19 |
20 | `;
21 |
22 | const TEST6 = `
23 |
24 | Alex
25 | La Bianca
26 |
27 |
28 | Ash
29 | Thrasher
30 |
31 |
32 | Jon
33 | Andrews
34 |
35 | `;
36 |
37 | const TEST7 = `
38 |
39 |
40 |
41 |
42 |
43 | `
44 | const TEST8 = `
45 |
46 | Alex
47 | US
48 | Troy
49 |
50 | `
51 |
52 | const TEST9 = `Alex`
53 |
54 | //Invalid XML
55 | const TEST10 = `AlexAlex`
57 | const TEST12 = `Jon`
58 |
59 | //nested repetition
60 | const TEST13 = ``;
61 | const TEST14 = `Alex`
62 |
63 | const TEST15 = `
64 |
65 | ccc
66 | yyy
67 |
68 |
69 | ddd
70 | yyy
71 |
72 | `;
73 |
74 | const TEST16 = `
75 |
76 | ccc
77 | bbb1
78 |
79 |
80 | ddd
81 | bbb2
82 |
83 | `
84 |
85 | const TEST17 = `
86 |
87 | ccc
88 | bbb1
89 |
90 |
91 | ddd
92 | bbb2
93 |
94 | `
95 |
96 |
97 | module.exports.MOCK_DATA = {
98 | TEST1: TEST1,
99 | TEST2: TEST2,
100 | TEST3: TEST3,
101 | TEST4: TEST4,
102 | TEST5: TEST5,
103 | TEST6: TEST6,
104 | TEST7: TEST7,
105 | TEST8: TEST8,
106 | TEST9: TEST9,
107 | I_TEST10: TEST10,
108 | I_TEST11: TEST11,
109 | I_TEST12: TEST12,
110 | TEST13: TEST13,
111 | TEST14: TEST14,
112 | TEST15: TEST15,
113 | TEST16: TEST16,
114 | TEST17: TEST17
115 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # xml-to-json-stream
2 |
3 | Simple module to convert XML to JSON with javascript
4 |
5 | ## Download
6 | `npm install xml-to-json-stream` or `npm install xml-to-json-stream -g`
7 | Installing the package globally allows you to convert xml file to json files via the command line.
8 | `cat config.xml | tojson > config.json` . If you want to ignore xml attribute simply add the `-no-attr` flag.
9 |
10 | ## Public API
11 |
12 | xml-to-json-stream only has two public methods `xmlToJson` and `createStream` .
13 | Use xmlToJson if you simply want to pass in some XML and access the resulting JSON in the callback function.
14 | Use createStream if you need to pipe some readable XML stream into a writable destination and convert the XML to JSON along the way.
15 |
16 | The module currently accepts `attributeMode` as an option, which defaults to `true ` . `attributeMode` decides whether XML attributes should
17 | be ignored or not.
18 |
19 | ```javascript
20 | const xmlToJson = require('xml-to-json-stream');
21 | const parser = xmlToJson({attributeMode:false});
22 |
23 | const xml = `
24 |
25 | Alex
26 |
27 | `
28 |
29 | parser.xmlToJson(xml, (err,json)=>{
30 | if(err) {
31 | //error handling
32 | }
33 |
34 | //json
35 | //{
36 | // employee: {
37 | // name: "Alex"
38 | // }
39 | //}
40 | });
41 |
42 | //the 'id' attribute of employee is ignored. If attributeMode was set to true or omitted, the json would have been:
43 | // {
44 | // employee: {
45 | // id: "123456",
46 | // name: "Alex"
47 | // }
48 | // }
49 |
50 | ```
51 |
52 | ### xmlToJson
53 |
54 | ```javascript
55 | const xmlToJson = require('xml-to-json-stream');
56 | const parser = xmlToJson({attributeMode:false});
57 |
58 | parser.xmlToJson(xml, (err,json)=>{
59 | if(err) {
60 | //error handling
61 | }
62 |
63 | //json is converted xml
64 | });
65 | ```
66 |
67 | ### createStream
68 |
69 | `createStream` allows you to create a Node.js Transform stream that can be written to and read from.
70 | Consider a hypothetical weather service that only responds with XML data. `createStream` will remove some of the boilerplate for you by
71 | letting you pipe service responses through the parser/stream.
72 |
73 | ```javascript
74 | const xmlToJson = require('xml-to-json-stream');
75 | const parser = xmlToJson();
76 | const stream = parser.createStream();
77 |
78 | const server = http.createServer((req,res)=>{
79 |
80 | http.get('http://someHypothecialXMLWeatherService/api', (response)=>{ //since response is a readable stream we can pipe it through our stream
81 | response.pipe(stream).pipe(res); //the client will receive the json representation of the XML response
82 | })
83 | })
84 | ```
85 |
86 | #### Or...
87 | ...If you have an XML file you want to convert on the fly via the command line `$ cat file.xml | node app`
88 |
89 | ```javascript
90 | const xmlToJson = require('xml-to-json-stream');
91 | const fs = require('fs');
92 |
93 | const parser = xmlToJson();
94 | const stream = parser.createStream();
95 | const jsonFile = fs.createWriteStream('jsonFile.json')
96 |
97 | process.stdin.pipe(stream).pipe(jsonFile);
98 | ```
99 |
100 |
101 | #### Additional Considerations
102 |
103 | * Comments are removed
104 | * Root elements are removed
105 | * If an element occurs multiple times in the same depth then that element will be represented as an array in the JSON
106 | ```
107 |
108 |
109 |
110 |
111 |
112 |
113 | will turn into...
114 |
115 | {
116 | xml: {
117 | employee: [
118 | {
119 | id: "123",
120 | name: "alex"
121 | },
122 | {
123 | id: "456",
124 | name: "jon"
125 | },
126 | {
127 | id: "789",
128 | name: "ashley"
129 | }
130 | ]
131 | }
132 | }
133 | ```
134 | * If an element contains a textNode and attributes the textNode will have the key textNode
135 | ```
136 | Alex
137 |
138 | will turn into...
139 |
140 | employee: {
141 | id: "98765",
142 | textNode: "Alex"
143 | }
144 |
145 | and ...
146 |
147 | Alex
148 |
149 | will turn into...
150 |
151 | employee: "Alex"
152 | ```
153 |
154 |
155 |
156 | License: MIT
157 |
158 |
--------------------------------------------------------------------------------
/lib/xmlToJson.js:
--------------------------------------------------------------------------------
1 | module.exports = traverse;
2 |
3 | function traverse(xml,attributeMode) {
4 | const tagFinder = new RegExp('<(.*?)[>|\\s|/]', 'g'); //find the current tag we are working on
5 |
6 | const json = {};
7 | let tagShouldBeArray = false;
8 |
9 | //recursion base case
10 | if(xml === '' || (xml.charAt(0) !== '<' && xml.charAt(xml.length-1) !== '>')) {
11 | return xml;
12 | }
13 |
14 | var currentLevelTags;
15 | var skip = 0;
16 | while((currentLevelTags = tagFinder.exec(xml)) !== null) {
17 | let selfClosing = false;
18 | const tag = currentLevelTags[1];
19 |
20 | const finishTag = ''+tag+'>';
21 | const input = currentLevelTags.input;
22 | const tagLength = input.indexOf('>',skip)+1;
23 |
24 | const start = currentLevelTags.index;
25 | const end = currentLevelTags.input.indexOf('>',start)+1;
26 | const currentTag = currentLevelTags.input.substring(start,end);
27 |
28 | selfClosing = isSelfClosing(currentTag);
29 |
30 | if(!validate(currentTag)) {
31 | const err = new Error('Invalid XML tag');
32 | throw err;
33 | }
34 | //const closingTagIndex = input.indexOf(finishTag,tagLength);
35 | const closingTagIndex = findClosingIndex(input,finishTag,tagLength);
36 | if(selfClosing === false && closingTagIndex < 0) {
37 | const err = new Error('Invalid XML');
38 | throw err;
39 | }
40 |
41 | let substring; //substring will be either all child tags or if self closing tag just a blank string. i.e: Alex : Alex will be the substring of the parent tag
42 | if(selfClosing) {
43 | substring = '';
44 | skip = currentTag.length + skip;
45 |
46 | } else {
47 | substring = input.substring(input.indexOf('>', skip)+1, closingTagIndex);
48 | skip = tagLength + substring.length + finishTag.length;
49 | }
50 |
51 |
52 | tagFinder.lastIndex = skip; //skip all child tags of current level
53 |
54 | if(!json[tag]) {
55 | json[tag] = {};
56 | } else {
57 | tagShouldBeArray = true;
58 | }
59 |
60 |
61 | let temporary = {};
62 | let attributes;
63 | if(attributeMode) {
64 | attributes = collectAttributes(currentTag);
65 | }
66 |
67 | //if currentTag contains attributes and attributeMode is enabled, attach them to json
68 | if(tagShouldBeArray && attributeMode) {
69 | temporary = attributes;
70 |
71 | } else if(!tagShouldBeArray && attributeMode) {
72 | for(let key in attributes) {
73 | json[tag][key] = attributes[key];
74 | }
75 | }
76 |
77 |
78 | //go one level deeper
79 | const next = traverse(substring,attributeMode);
80 |
81 | //when returning from recursion, build up the json
82 |
83 | if(typeof next === 'object') {
84 | //const key = Object.keys(next)[0];
85 | if(tagShouldBeArray && !json[tag].length) {
86 | const temp = json[tag];
87 | json[tag] = [temp];
88 | const nextObj = {}
89 | for(let key in next) {
90 | nextObj[key] = next[key];
91 | }
92 | temporary = {...temporary,...nextObj};
93 | json[tag].push(temporary);
94 | }else if(tagShouldBeArray) {
95 | const nextObj = {};
96 | for(let key in next) {
97 | nextObj[key] = next[key];
98 | }
99 | temporary = {...temporary,...nextObj};
100 | json[tag].push(temporary);
101 | }else {
102 | for(let key in next) {
103 | json[tag][key] = next[key];
104 | }
105 | }
106 |
107 |
108 | } else if(Object.keys(json[tag]).length>0) {
109 |
110 | if((tagShouldBeArray && !json[tag].length) || typeof json[tag] === 'string') {
111 | const temp = json[tag];
112 | json[tag] = [temp];
113 |
114 | if(typeof next !== 'object') {
115 | if(Object.keys(temporary).length === 0) {
116 | json[tag].push(next);
117 | } else {
118 | // temporary['data'] = next;
119 | if(next !== '') {
120 | temporary['textNode'] = next;
121 | }
122 | json[tag].push(temporary);
123 | }
124 |
125 |
126 | } else {
127 | temporary = {...temporary,next};
128 | json[tag].push(next);
129 | }
130 | //json[tag].push(next);
131 |
132 | } else if(tagShouldBeArray) {
133 | //json[tag].push(next);
134 | if(typeof next !== 'object') {
135 | if(Object.keys(temporary).length === 0) {
136 | json[tag].push(next);
137 | } else {
138 | //temporary['data'] = next;
139 | if(next !== '') {
140 | temporary['textNode'] = next;
141 | }
142 | json[tag].push(temporary);
143 | }
144 |
145 |
146 | } else {
147 | temporary = {...temporary,next};
148 | json[tag].push(next);
149 | }
150 |
151 | } else {
152 | if(next !== '') {
153 | json[tag] = {
154 | ...json[tag],
155 | textNode: next
156 | }
157 | }
158 |
159 | }
160 |
161 | } else {
162 | if(tagShouldBeArray && typeof json[tag] !== 'object') {
163 | const temp = json[tag];
164 | json[tag] = [];
165 | json[tag].push(...temp,next);
166 | }else {
167 | json[tag] = next;
168 | }
169 | //json[tag] = next;
170 | }
171 |
172 | }
173 |
174 |
175 | return json;
176 | }
177 |
178 |
179 |
180 |
181 | //Helper methods
182 |
183 | //Determine if a tag is self closing or not. Could be improved
184 | function isSelfClosing(currentTag) {
185 | if(currentTag.indexOf('/>') > -1) {
186 | return true;
187 | }
188 | return false;
189 | }
190 |
191 | //Collect all the attributes of the current tag and return an object in form of {attribute:values}
192 | function collectAttributes(currentTag) {
193 | const attributeFinder = new RegExp('(\\S*)="(.*?)"', 'g');
194 | const foundAttributes = {};
195 |
196 | let attributes
197 | while((attributes = attributeFinder.exec(currentTag)) !== null) {
198 | const key = attributes[1];
199 | const value = attributes[2];
200 |
201 | foundAttributes[key] = value;
202 | }
203 |
204 | return foundAttributes;
205 | }
206 |
207 | function validate(currentTag) {
208 | if((currentTag.charAt(0) === '<' && currentTag.charAt(1) === '?') && (currentTag.charAt(currentTag.length-1) === '>' && currentTag.charAt(currentTag.length-2) === '?')) {
209 | return true;
210 | }
211 |
212 | if(currentTag.charAt(0) === '<' && (currentTag.charAt(currentTag.length-2)+currentTag.charAt(currentTag.length-1) === '/>' || currentTag.charAt(currentTag.length-1) === '>')) {
213 | return true;
214 | }
215 |
216 | return false;
217 | }
218 |
219 |
220 | function findClosingIndex(searchString,tag,start) {
221 |
222 | const openinTag = tag.replace('', '<').replace('>', '');
223 | let closingIndex = searchString.indexOf(tag,start);
224 | let openingIndex = searchString.indexOf(openinTag,start);
225 |
226 | if(closingIndex < openingIndex) {
227 | return closingIndex;
228 | }
229 |
230 | const sub = searchString.substr(openingIndex,closingIndex-openingIndex);
231 |
232 | if(!sub.match(new RegExp(openinTag + "\\W"))) {
233 | return closingIndex;
234 | }
235 |
236 | while(closingIndex > 0) {
237 | const tempIndex = searchString.indexOf(tag,closingIndex+1);
238 | if(tempIndex > 0) {
239 | closingIndex = tempIndex;
240 | } else {
241 | break;
242 | }
243 | }
244 |
245 | return closingIndex;
246 | }
--------------------------------------------------------------------------------
/spec/traverse-spec.js:
--------------------------------------------------------------------------------
1 | const traverse = require('../lib/xmlToJson');
2 | const mockData = require('./mockXML').MOCK_DATA;
3 | const clean = require('../lib/cleanXML');
4 |
5 |
6 | describe('TRAVERSE: With Attributes', ()=>{
7 | const attributeMode = true;
8 |
9 |
10 | it('should collect all 3 attributes of the "employee" tag', ()=>{
11 |
12 | const cleanXML = clean(mockData.TEST1)
13 | const json = traverse(cleanXML,attributeMode);
14 |
15 | const result = {
16 | employee : {
17 | id: "12345",
18 | building: '1',
19 | geo: "US",
20 | name: "Alex"
21 | }
22 | }
23 |
24 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
25 | });
26 |
27 | it('should create an array if the same tag exist on the same level', ()=>{
28 | //const cleanXML = mockData.TEST2.replace(/>\s*<');
29 | const cleanXML = clean(mockData.TEST2)
30 | const json = traverse(cleanXML,attributeMode);
31 |
32 | const result = {
33 | employee: [
34 | {
35 | id: '12345',
36 | name: 'Alex',
37 | },
38 | {
39 | id: '56789',
40 | name: 'Jon'
41 | }
42 | ]
43 | }
44 |
45 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
46 | });
47 |
48 | it('should parse xml without attributes even if attributeMode is enabled', ()=>{
49 | const cleanXML = clean(mockData.TEST3)
50 | const json = traverse(cleanXML,attributeMode);
51 |
52 | const result = {
53 | employee: {
54 | name: 'Alex',
55 | age: '25'
56 | }
57 | }
58 |
59 | expect(JSON.stringify(json)).toBe(JSON.stringify(result))
60 | })
61 |
62 | it('should read the single attribute', ()=>{
63 | const cleanXML = clean(mockData.TEST4)
64 | const json = traverse(cleanXML,attributeMode);
65 |
66 | const result = {
67 | employee: {
68 | id: '12345'
69 | }
70 | }
71 |
72 | expect(JSON.stringify(json)).toBe(JSON.stringify(result))
73 | });
74 |
75 | it('should pass sanity check', ()=>{
76 |
77 | const cleanXML = clean(mockData.TEST5)
78 | const converted = traverse(cleanXML, attributeMode);
79 |
80 | const result = {
81 | employee: {
82 | name: "Alex"
83 | },
84 | role: "Software Dev",
85 | locality: {
86 | country: "US",
87 | region: "TX",
88 | city: "Austin"
89 | }
90 | }
91 |
92 | expect(JSON.stringify(converted)).toBe(JSON.stringify(result));
93 | })
94 |
95 | it('should create an array of employees where each contain an array of names', ()=>{
96 | const cleanXML = clean(mockData.TEST6);
97 | const json = traverse(cleanXML,attributeMode);
98 |
99 | const result = {
100 | employee: [
101 | {
102 | id: "12345",
103 | name: [
104 | { type: 'first', textNode: 'Alex'},
105 | { type: 'last', textNode: 'La Bianca'}
106 | ]
107 |
108 |
109 | },
110 | {
111 | id: "98765",
112 | name: [
113 | { type: 'first', textNode: 'Ash'},
114 | { type: 'last', textNode: 'Thrasher'}
115 | ]
116 | },
117 | {
118 | id: "12332",
119 | name: [
120 | { type: 'first', textNode: 'Jon'},
121 | { type: 'last', textNode: 'Andrews'}
122 | ]
123 | }
124 | ]
125 | };
126 |
127 | expect(JSON.stringify(json)).toBe(JSON.stringify(result))
128 | });
129 |
130 |
131 | it('should create an object with xml key that contains one property which is an array of length 3', ()=>{
132 | const cleanXML = clean(mockData.TEST7);
133 | const json = traverse(cleanXML,attributeMode);
134 |
135 | const result = {
136 | xml: {
137 | employee: [
138 | {
139 | id: '123',
140 | name: 'alex'
141 | },
142 | {
143 | id: '456',
144 | name: 'jon'
145 | },
146 | {
147 | id: '789',
148 | name: 'ashley'
149 | }
150 | ]
151 | }
152 | }
153 |
154 | expect(JSON.stringify(json)).toBe(JSON.stringify(result))
155 | });
156 |
157 | it('should process an array like element if it is out of order', ()=>{
158 | const cleanXML = clean(mockData.TEST8);
159 | const json = traverse(cleanXML,attributeMode);
160 |
161 | const result = {
162 | xml: {
163 | employee: [
164 | {name: 'Alex'},
165 | {name: 'Troy'}
166 | ],
167 | location: 'US'
168 | }
169 | };
170 |
171 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
172 | })
173 |
174 | it('should process a single xml element', ()=>{
175 | const cleanXML = clean(mockData.TEST9);
176 | const json = traverse(cleanXML,attributeMode);
177 |
178 | const result = {
179 | employee: {
180 | id: "98765",
181 | textNode: "Alex"
182 | }
183 | }
184 |
185 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
186 | })
187 |
188 |
189 | });
190 |
191 |
192 |
193 |
194 | describe('TRAVERSE: Without Attributes', ()=>{
195 | const attributeMode = false;
196 |
197 | it('should not collect any attributes', ()=>{
198 | const cleanXML = clean(mockData.TEST1)
199 | const json = traverse(cleanXML,attributeMode);
200 |
201 | const result = {
202 | employee : {
203 | name: "Alex"
204 | }
205 | }
206 |
207 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
208 | });
209 |
210 | it('should creeate an array', ()=>{
211 | const cleanXML = clean(mockData.TEST2)
212 | const json = traverse(cleanXML,attributeMode);
213 |
214 | const result = {
215 | employee: [
216 | {
217 | name: 'Alex',
218 | },
219 | {
220 | name: 'Jon'
221 | }
222 | ]
223 | }
224 |
225 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
226 | });
227 |
228 | it('should not read the single attribute', ()=>{
229 | const cleanXML = clean(mockData.TEST4)
230 | const json = traverse(cleanXML,attributeMode);
231 |
232 | const result = {
233 | employee: ""
234 | }
235 |
236 | expect(JSON.stringify(json)).toBe(JSON.stringify(result))
237 | });
238 |
239 |
240 | it('should create an array of employees where each contain an array of names', ()=>{
241 | const cleanXML = clean(mockData.TEST6);
242 | const json = traverse(cleanXML,attributeMode);
243 |
244 | const result = {
245 | employee: [
246 | {
247 | name: [
248 | "Alex",
249 | "La Bianca"
250 | ]
251 |
252 |
253 | },
254 | {
255 | name: [
256 | "Ash",
257 | "Thrasher"
258 | ]
259 | },
260 | {
261 | name: [
262 | "Jon",
263 | "Andrews"
264 | ]
265 | }
266 | ]
267 | };
268 |
269 | expect(JSON.stringify(json)).toBe(JSON.stringify(result))
270 | })
271 |
272 | it('should create an object with xml key that contains one property which is an array of length 3', ()=>{
273 | const cleanXML = clean(mockData.TEST7);
274 | const json = traverse(cleanXML,attributeMode);
275 |
276 | const result = {
277 | xml: {
278 | employee: ""
279 | }
280 | }
281 |
282 | expect(JSON.stringify(json)).toBe(JSON.stringify(result))
283 | });
284 |
285 | it('should process a single xml element', ()=>{
286 | const cleanXML = clean(mockData.TEST9);
287 | const json = traverse(cleanXML,attributeMode);
288 |
289 | const result = {
290 | employee: "Alex"
291 | }
292 |
293 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
294 | });
295 | });
296 |
297 |
298 | describe('ERRORS: Invalid XML', ()=>{
299 | const attributeMode = true;
300 |
301 | it('should throw an error with invalid xml 1', ()=>{
302 | const cleanXML = clean(mockData.I_TEST10);
303 |
304 | expect(traverse.bind(null,cleanXML,attributeMode)).toThrowError(Error, "Invalid XML")
305 |
306 | });
307 |
308 | it('should throw an error with invalid xml 2', ()=>{
309 | const cleanXML = clean(mockData.I_TEST11);
310 |
311 | expect(traverse.bind(null,cleanXML,attributeMode)).toThrowError(Error, "Invalid XML")
312 |
313 | });
314 |
315 | it('should throw an error with invalid xml 3', ()=>{
316 | const cleanXML = clean(mockData.I_TEST12);
317 |
318 | expect(traverse.bind(null,cleanXML,attributeMode)).toThrowError(Error, "Invalid XML")
319 |
320 | });
321 | });
322 |
323 |
324 |
325 | describe('NESTING: Repetions', ()=>{
326 | const attributeMode = true;
327 |
328 | it('should correctly parse nested repeated xml tags', ()=>{
329 | const cleanXML = clean(mockData.TEST13);
330 | const json = traverse(cleanXML,attributeMode);
331 |
332 | const result = {
333 | employee: {
334 | id: "98765",
335 | name: "alex",
336 | employee: {
337 | id: "123",
338 | name: "jon"
339 | }
340 | }
341 | }
342 |
343 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
344 |
345 |
346 | });
347 |
348 | it('should throw an error with invalid xml 2', ()=>{
349 | const cleanXML = clean(mockData.TEST14);
350 | const json = traverse(cleanXML,attributeMode);
351 |
352 | const result = {
353 | employee: {
354 | id: "98765",
355 | name: "alex",
356 | employee: "Alex"
357 | }
358 | }
359 |
360 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
361 |
362 | });
363 |
364 | it('should distinguish between similar tags ( vs. )', ()=>{
365 | const cleanXML = clean(mockData.TEST15);
366 | const json = traverse(cleanXML,attributeMode);
367 |
368 | const result = {
369 | aaa: {
370 | bbb: [
371 | {
372 | bbb1: "ccc",
373 | yyy: "yyy"
374 | },
375 | {
376 | ddd: "ddd",
377 | yyy: "yyy"
378 | }
379 | ]
380 | }
381 | }
382 |
383 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
384 | });
385 |
386 | it('should distinguis between similar tags [2] ( vs )', ()=>{
387 | const cleanXML = clean(mockData.TEST16);
388 | const json = traverse(cleanXML,attributeMode);
389 |
390 | const result = {
391 | aaa: {
392 | bbb: [
393 | {
394 | ccc: "ccc",
395 | bbb1: "bbb1"
396 | },
397 | {
398 | ddd: "ddd",
399 | bbb2: "bbb2"
400 | }
401 | ]
402 | }
403 | }
404 |
405 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
406 | })
407 |
408 |
409 | it('should distinguis between similar tags [3] [attribute=true] ( vs )', ()=>{
410 | const cleanXML = clean(mockData.TEST17);
411 | const json = traverse(cleanXML,attributeMode);
412 |
413 | const result = {
414 | aaa: {
415 | bbb: [
416 | {
417 | locale: "Austin",
418 | ccc: "ccc",
419 | bbb1: {
420 | locale: "Berlin",
421 | textNode: "bbb1"
422 | },
423 | },
424 | {
425 | locale: "San Francisco",
426 | ddd: "ddd",
427 | bbb2: {
428 | locale: "London",
429 | textNode: "bbb2"
430 | },
431 | }
432 | ]
433 | }
434 | }
435 |
436 | expect(JSON.stringify(json)).toBe(JSON.stringify(result));
437 | })
438 |
439 | });
440 |
441 |
442 |
--------------------------------------------------------------------------------