From 4179b4010afaa68c0eff904d5d07f13423501e28 Mon Sep 17 00:00:00 2001 From: ztimson Date: Sat, 4 Apr 2026 18:20:44 -0400 Subject: [PATCH] Revert "added fast-xml-parser for testing" This reverts commit 15ac52b6a0418e60b1d888531f97734d978b3e29. --- Dockerfile | 2 -- src/xml.ts | 54 +++++---------------------------- tests/xml.spec.ts | 77 ++++++++++++++++++----------------------------- 3 files changed, 38 insertions(+), 95 deletions(-) diff --git a/Dockerfile b/Dockerfile index 864331d..0a82b68 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,4 @@ RUN if [ ! -d "node_modules" ]; then npm i; fi && \ # Use Nginx to serve FROM nginx:1.23-alpine -RUN npm i fast-xml-parser - COPY --from=build /app/docs /usr/share/nginx/html diff --git a/src/xml.ts b/src/xml.ts index 2f3ef02..d415fdf 100644 --- a/src/xml.ts +++ b/src/xml.ts @@ -3,11 +3,6 @@ * @param {string} xml - The XML string to parse * @returns {Object} An object with `tag`, `attributes`, and `children` properties */ -/** - * Parses an XML string into a structured JavaScript object (fast-xml-parser style). - * @param {string} xml - The XML string to parse - * @returns {Object} An object matching fast-xml-parser output format - */ export function fromXml(xml: string) { xml = xml.trim(); let pos = 0; @@ -33,7 +28,7 @@ export function fromXml(xml: string) { if(xml[pos] === '/' && xml[pos + 1] === '>') { pos += 2; // skip /> - return { [tagName]: Object.keys(attributes).length > 0 ? { '@_': attributes } : '' }; + return { tag: tagName, attributes, children: [] }; } pos++; // skip > @@ -50,54 +45,17 @@ export function fromXml(xml: string) { const child = parseNode(); if(child) children.push(child); } - - // Build fast-xml-parser style output - const result: any = {}; - - // Add attributes with @_ prefix - if(Object.keys(attributes).length > 0) { - for(const [key, value] of Object.entries(attributes)) { - result[`@_${key}`] = value; - } - } - - // Process children - if(children.length === 1 && typeof children[0] === 'string') { - // Single text node - if(Object.keys(attributes).length > 0) { - result['#text'] = children[0]; - return { [tagName]: result }; - } - return { [tagName]: children[0] }; - } - - // Group children by tag name - for(const child of children) { - if(typeof child === 'string') { - result['#text'] = child; - } else { - for(const [childTag, childValue] of Object.entries(child)) { - if(result[childTag]) { - if(!Array.isArray(result[childTag])) { - result[childTag] = [result[childTag]]; - } - result[childTag].push(childValue); - } else { - result[childTag] = childValue; - } - } - } - } - - return { [tagName]: Object.keys(result).length > 0 ? result : '' }; + return { tag: tagName, attributes, children }; } + /** Parses and returns the tag name at the current position */ function parseTagName() { let name = ''; while (pos < xml.length && /[a-zA-Z0-9_:-]/.test(xml[pos])) name += xml[pos++]; return name; } + /** Parses and returns an object containing all attributes at the current position */ function parseAttributes() { const attrs: any = {}; while (pos < xml.length) { @@ -118,6 +76,7 @@ export function fromXml(xml: string) { return attrs; } + /** Parses and returns text content, or null if empty */ function parseText() { let text = ''; while (pos < xml.length && xml[pos] !== '<') text += xml[pos++]; @@ -125,16 +84,19 @@ export function fromXml(xml: string) { return text ? escapeXml(text, true) : null; } + /** Skips over XML declaration () */ function parseDeclaration() { while (xml[pos] !== '>') pos++; pos++; } + /** Skips over XML comments () */ function parseComment() { while (!(xml[pos] === '-' && xml[pos + 1] === '-' && xml[pos + 2] === '>')) pos++; pos += 3; } + /** Advances position past any whitespace characters */ function skipWhitespace() { while (pos < xml.length && /\s/.test(xml[pos])) pos++; } diff --git a/tests/xml.spec.ts b/tests/xml.spec.ts index ef95851..e78e353 100644 --- a/tests/xml.spec.ts +++ b/tests/xml.spec.ts @@ -5,20 +5,22 @@ describe('XML Parser', () => { it('should parse simple tag', () => { const xml = ''; const result = fromXml(xml); - expect(result).toEqual({ root: '' }); + expect(result).toEqual({ tag: 'root', attributes: {}, children: [] }); }); it('should parse self-closing tag', () => { const xml = ''; const result = fromXml(xml); - expect(result).toEqual({ item: '' }); + expect(result).toEqual({ tag: 'item', attributes: {}, children: [] }); }); it('should parse tag with attributes', () => { const xml = ''; const result = fromXml(xml); expect(result).toEqual({ - user: { '@_id': '1', '@_name': 'someone' } + tag: 'user', + attributes: { id: '1', name: 'someone' }, + children: [] }); }); @@ -26,7 +28,9 @@ describe('XML Parser', () => { const xml = 'someone@example.com'; const result = fromXml(xml); expect(result).toEqual({ - email: 'someone@example.com' + tag: 'email', + attributes: {}, + children: ['someone@example.com'] }); }); @@ -34,75 +38,54 @@ describe('XML Parser', () => { const xml = 'text'; const result = fromXml(xml); expect(result).toEqual({ - root: { - child: 'text' - } + tag: 'root', + attributes: {}, + children: [ + { tag: 'child', attributes: {}, children: ['text'] } + ] }); }); it('should parse multiple children', () => { const xml = ''; const result = fromXml(xml); - expect(result.root.a).toBe(''); - expect(result.root.b).toBe(''); - expect(result.root.c).toBe(''); - }); - - it('should parse repeated tags as arrays', () => { - const xml = '123'; - const result = fromXml(xml); - expect(result).toEqual({ - root: { - item: ['1', '2', '3'] - } - }); + expect(result.children.length).toBe(3); + expect(result.children[0]).toEqual({ tag: 'a', attributes: {}, children: [] }); }); it('should skip XML declaration', () => { const xml = ''; const result = fromXml(xml); - expect(result.root).toBe(''); + expect(result.tag).toBe('root'); }); it('should skip comments', () => { const xml = ''; const result = fromXml(xml); - expect(result).toEqual({ - root: { child: '' } - }); + expect(result.children.length).toBe(1); + expect(result.children[0].tag).toBe('child'); }); it('should handle escaped characters', () => { const xml = '<hello> & "world"'; const result = fromXml(xml); - expect(result.text).toBe(' & "world"'); - }); - - it('should parse tag with attributes and text', () => { - const xml = 'John'; - const result = fromXml(xml); - expect(result).toEqual({ - user: { - '@_id': '1', - '#text': 'John' - } - }); + expect(result.children[0]).toBe(' & "world"'); }); it('should parse complex nested structure', () => { const xml = ` - - - someone@example.com - - - - `; + + + someone@example.com + + + + `; const result = fromXml(xml); - expect(result.root.user['@_id']).toBe('1'); - expect(result.root.user['@_name']).toBe('someone'); - expect(result.root.user.email).toBe('someone@example.com'); - expect(result.root.user.active).toBe(''); + expect(result.tag).toBe('root'); + expect(result.children[0].tag).toBe('user'); + expect(result.children[0].attributes.name).toBe('someone'); + expect(result.children[0].children.length).toBe(2); }); });