UNPKG

rapid-xml-to-json

Version:

Fast, dependency-free XML-parser for parsing XML structures into JSON. Optimised for speed and low memory usage.

607 lines (539 loc) 16.3 kB
import { xmlToJson } from "./index"; // TODO: Benchmark tests describe('Test of parse', () => { test('parses a single attribute', () => { const xml = `<root attr="value">Content</root>`; const expected = { root: { __parent: expect.any(Object), $attrs: { attr: "value" }, "#text": "Content", }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('parses attributes in a self-closing tag', () => { const xml = `<root attr="value"/>`; const expected = { root: { __parent: expect.any(Object), $attrs: { attr: "value" }, }, }; let actual = xmlToJson(xml) expect(actual).toEqual(expected); }); test('parses multiple attributes', () => { const xml = `<root attr1="value1" attr2="value2">Content</root>`; const expected = { root: { __parent: expect.any(Object), $attrs: { attr1: "value1", attr2: "value2" }, "#text": "Content", }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('parses attributes in nested elements', () => { const xml = `<root><child attr="value">Content</child></root>`; const expected = { root: { __parent: expect.any(Object), child: { __parent: expect.any(Object), $attrs: { attr: "value" }, "#text": "Content", }, }, }; let actual = xmlToJson(xml) expect(actual).toEqual(expected); }); it('Correctly parses an XML structure', () => { const xml = `<root><a></a><b></b></root>` const json = xmlToJson(xml); expect(json.root).toHaveProperty('a'); expect(json.root).toHaveProperty('b'); }); it('Correctly parses CDATA', () => { const aData = '<Data from CDATA>' const xml = `<root><a><![CDATA[${aData}]]></a><b>Test</b>` const json = xmlToJson(xml); expect(json.root.a).toEqual({__parent: expect.any(Object), '#text': "<Data from CDATA>"}); expect(json.root.b).toEqual({__parent: expect.any(Object), '#text': "Test"}); }); it('Correctly ignores comments, DOCTYPE, stylesheets', () => { const xml = `<root><a><!--Test comment--><c></c></a><b></b></root>` const json = xmlToJson(xml); expect(json.root).toHaveProperty('a'); expect(json.root).toHaveProperty('b'); expect(json.root.a).toHaveProperty('c'); }); it('Correctly parses lists of elements', () => { const xml = `<root><a></a><a></a><a/><b></b></root>` const json = xmlToJson(xml); expect(json.root).toHaveProperty('b'); expect(json.root.a.length).toBe(3); }); test('parses deeply nested structures', () => { const xml = ` <root> <level1> <level2> <level3> <level4>Deep content</level4> </level3> </level2> </level1> </root> `; const expected = { root: { __parent: expect.any(Object), level1: { __parent: expect.any(Object), level2: { __parent: expect.any(Object), level3: { __parent: expect.any(Object), level4: {__parent: expect.any(Object),'#text': 'Deep content'}, }, }, }, }, }; const actual = xmlToJson(xml) expect(actual).toEqual(expected); }); test('handles CDATA-heavy XML', () => { const xml = ` <root> <data><![CDATA[Some CDATA content]]></data> <data><![CDATA[<tag>Inside CDATA</tag>]]></data> <data><![CDATA[]]></data> </root> `; const expected = { root: { __parent: expect.any(Object), data: [ {__parent: expect.any(Object), '#text':'Some CDATA content'}, {__parent: expect.any(Object), '#text':'<tag>Inside CDATA</tag>'}, {__parent: expect.any(Object), '#text':''}, ], }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('parses XML with variations in whitespace formatting', () => { const xml = ` <root> <item> Content with leading and trailing spaces </item> <item> Content with line breaks </item> <item> </item> </root> `; const expected = { root: { __parent: expect.any(Object), item: [ {__parent: expect.any(Object), '#text':' Content with leading and trailing spaces '}, {__parent: expect.any(Object), '#text':` Content with line breaks `}, {__parent: expect.any(Object)} ], }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('handles empty tags and self-closing tags', () => { const xml = ` <root> <empty></empty> <selfClosing /> <nestedSelfClosing> <child /> </nestedSelfClosing> </root> `; const expected = { root: { __parent: expect.any(Object), empty: {__parent: expect.any(Object)}, selfClosing: {__parent: expect.any(Object)}, nestedSelfClosing: { __parent: expect.any(Object), child: {__parent: expect.any(Object)}, }, }, }; const actual = xmlToJson(xml) expect(actual).toEqual(expected); }); test('skips comments, processing instructions, and DOCTYPE declarations', () => { const xml = ` <?xml version="1.0"?> <!DOCTYPE note SYSTEM "Note.dtd"> <root> <!-- This is a comment --> <item>Valid content</item> </root> `; const expected = { root: { __parent: expect.any(Object), item: {__parent: expect.any(Object), '#text': 'Valid content'}, }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('handles mixed CDATA and regular content in a single document', () => { const xml = ` <root> <data><![CDATA[CDATA content]]></data> <data>Regular content</data> <data><![CDATA[More CDATA]]></data> </root> `; const expected = { root: { __parent: expect.any(Object), data: [ {__parent: expect.any(Object), '#text': 'CDATA content'}, {__parent: expect.any(Object), '#text': 'Regular content'}, {__parent: expect.any(Object), '#text': 'More CDATA'}, ], }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('handles multiple sibling elements with the same name', () => { const xml = ` <root> <item>First</item> <item>Second</item> <item>Third</item> </root> `; const expected = { root: { __parent: expect.any(Object), item: [{__parent: expect.any(Object), '#text':'First'}, {__parent: expect.any(Object), '#text': 'Second'}, {__parent: expect.any(Object), '#text':'Third'}], }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('handles deeply nested structures with mixed empty and populated elements', () => { const xml = ` <root> <level1> <level2> <level3> <empty /> <populated>Content</populated> </level3> </level2> </level1> </root> `; const expected = { root: { __parent: expect.any(Object), level1: { __parent: expect.any(Object), level2: { __parent: expect.any(Object), level3: { __parent: expect.any(Object), empty: {__parent: expect.any(Object)}, populated: {__parent: expect.any(Object), '#text': 'Content'}, }, }, }, }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('handles XML with no content inside tags', () => { const xml = ` <root> <empty1></empty1> <empty2 /> </root> `; const expected = { root: { __parent: expect.any(Object), empty1: {__parent: expect.any(Object)}, empty2: {__parent: expect.any(Object)}, }, }; const actual = xmlToJson(xml) expect(actual).toEqual(expected); }); test('parses CDATA with special characters', () => { const xml = ` <root> <data><![CDATA[<special>&characters</special>]]></data> </root> `; const expected = { root: { __parent: expect.any(Object), data: {__parent: expect.any(Object), '#text': '<special>&characters</special>'}, }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('handles documents with leading and trailing whitespace', () => { const xml = ` <root> <item>Content</item> </root> `; const expected = { root: { __parent: expect.any(Object), item: {__parent: expect.any(Object), '#text': 'Content'}, }, }; const actual = xmlToJson(xml) expect(actual).toEqual(expected); }); test('handles empty root element', () => { const xml = ` <root></root> `; const expected = { root: {__parent: expect.any(Object)}, }; expect(xmlToJson(xml)).toEqual(expected); }); test('handles large documents', () => { const xml = ` <root> ${'<item>Content</item>'.repeat(10000)} </root> `; const expected = { root: { __parent: expect.any(Object), item: Array(10000).fill({__parent: expect.any(Object), '#text':'Content'}), }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('handles multiple levels of CDATA', () => { const xml = ` <root> <data><![CDATA[Level 1]]><![CDATA[ and Level 2]]></data> </root> `; const expected = { root: { __parent: expect.any(Object), data: {__parent: expect.any(Object), '#text': 'Level 1 and Level 2'}, }, }; const actual = xmlToJson(xml); expect(actual).toEqual(expected); }); test('handles mixed test and CDATA', () => { const xml = ` <root> <data>Test <![CDATA[Level 1]]>here<![CDATA[ and Level 2]]>there</data> </root> `; const expected = { root: { __parent: expect.any(Object), data: {__parent: expect.any(Object), '#text': 'Test Level 1here and Level 2there'}, }, }; const actual = xmlToJson(xml); expect(actual).toEqual(expected); }); test('ignores extraneous characters outside root element', () => { const xml = ` <!-- Comment --> <root> <item>Valid content</item> </root> <!-- Another comment --> `; const expected = { root: { __parent: expect.any(Object), item: {__parent: expect.any(Object), '#text': 'Valid content'}, }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('handles XML with unconventional spacing and line breaks', () => { const xml = ` <root> <item >Content</item > <item>Another</item > </root> `; const expected = { root: { __parent: expect.any(Object), item: [{__parent: expect.any(Object), '#text': 'Content'}, {__parent: expect.any(Object), '#text':'Another'}], }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('ignores invalid comments and CDATA-like content outside valid constructs', () => { const xml = ` <root> <item>Valid content</item> <!-- This is not <![CDATA[valid]] --> <!-- This is also not valid CDATA ]]> --> </root> `; const expected = { root: { __parent: expect.any(Object), item: {__parent: expect.any(Object), '#text': 'Valid content'}, }, }; const actual = xmlToJson(xml) expect(actual).toEqual(expected); }); test('parses attributes with special characters', () => { const xml = `<root attr="value with spaces" another-attr="value&chars!">Content</root>`; const expected = { root: { __parent: expect.any(Object), $attrs: { attr: "value with spaces", "another-attr": "value&chars!", }, "#text": "Content", }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('parses attributes with empty values', () => { const xml = `<root attr1="" attr2="non-empty">Content</root>`; const expected = { root: { __parent: expect.any(Object), $attrs: { attr1: "", attr2: "non-empty", }, "#text": "Content", }, }; const actual = xmlToJson(xml) expect(actual).toEqual(expected); }); test('parses element with only attributes and no text content', () => { const xml = `<root attr1="value1" attr2="value2"></root>`; const expected = { root: { __parent: expect.any(Object), $attrs: { attr1: "value1", attr2: "value2", }, }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('parses attributes with mixed whitespace formatting', () => { const xml = `<root attr1="value1" attr2=" value2 ">Content</root>`; const expected = { root: { __parent: expect.any(Object), $attrs: { attr1: "value1", attr2: " value2 ", // Preserves the spaces }, "#text": "Content", }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('parses attributes with quotes inside the value', () => { const xml = `<root attr="value with \\"quotes\\" inside">Content</root>`; const expected = { root: { __parent: expect.any(Object), $attrs: { attr: 'value with \\"quotes\\" inside', // Preserves internal quotes }, "#text": "Content", }, }; const actual = xmlToJson(xml) expect(actual).toEqual(expected); }); test('parses attributes with multiple equal signs in the value', () => { const xml = `<root attr="value=with=equals">Content</root>`; const expected = { root: { __parent: expect.any(Object), $attrs: { attr: "value=with=equals", }, "#text": "Content", }, }; expect(xmlToJson(xml)).toEqual(expected); }); test('Handles array of self-closing elements', () => { const xml = `<Stemmer> <Parti Id="5891" Bogstav="A" Navn="A. Socialdemokratiet" StemmerAntal="86226" StemmerPct="19"/> <Parti Id="5893" Bogstav="B" Navn="B. Radikale Venstre" StemmerAntal="34031" StemmerPct="7.5"/> <Parti Id="5895" Bogstav="C" Navn="C. Det Konservative Folkeparti" StemmerAntal="22797" StemmerPct="5"/> </Stemmer>` const expected = { Stemmer: { __parent: expect.any(Object), Parti: [ { __parent: expect.any(Object), $attrs: { Id: "5891", Bogstav: "A", Navn: "A. Socialdemokratiet", StemmerAntal: "86226", StemmerPct: "19", }, }, { __parent: expect.any(Object), $attrs: { Id: "5893", Bogstav: "B", Navn: "B. Radikale Venstre", StemmerAntal: "34031", StemmerPct: "7.5", }, }, { __parent: expect.any(Object), $attrs: { Id: "5895", Bogstav: "C", Navn: "C. Det Konservative Folkeparti", StemmerAntal: "22797", StemmerPct: "5", }, }, ], } } expect(xmlToJson(xml)).toEqual(expected); }) });