diff --git a/src/interchange/xml.zig b/src/interchange/xml.zig index 66fe45634e..18937b02d3 100644 --- a/src/interchange/xml.zig +++ b/src/interchange/xml.zig @@ -235,6 +235,16 @@ const Parser = struct { // Build result with cleaner structure - no __name or __children const trimmed_text = std.mem.trim(u8, text_parts.items, " \t\n\r"); + + // If element has only text content (no attributes, no children), return text directly + if (attributes.items.len == 0 and children.items.len == 0 and trimmed_text.len > 0) { + const text_expr = try self.createStringExpr(text_parts.items); + return ChildElement{ + .tag_name = tag_name_slice, + .element = text_expr, + }; + } + var properties = std.ArrayList(G.Property).init(self.allocator); // Add attributes directly as properties @@ -242,16 +252,27 @@ const Parser = struct { try properties.appendSlice(attributes.items); } - // Add children as direct properties - if (children.items.len > 0) { + // Handle mixed content (text + children) - use __children array only + if (children.items.len > 0 and text_parts.items.len > 0 and trimmed_text.len > 0) { + // Mixed content: use __children array only + var child_array = std.ArrayList(Expr).init(self.allocator); + for (children.items) |child| { + try child_array.append(child.element); + } + const children_array = Expr.init(E.Array, .{ .items = .fromList(child_array) }, .Empty); + const children_key = try self.createStringExpr("__children"); + try properties.append(.{ .key = children_key, .value = children_array }); + } else if (children.items.len > 0) { + // Children only: add as direct properties try self.addChildrenAsProperties(&properties, children.items); - } - - // Add text content if present and no children - if (text_parts.items.len > 0 and trimmed_text.len > 0 and children.items.len == 0) { + } else if (text_parts.items.len > 0 and trimmed_text.len > 0 and attributes.items.len > 0) { + // Attributes + text only: use single-element __children array for consistency const text_expr = try self.createStringExpr(text_parts.items); - const text_key = try self.createStringExpr("__text"); - try properties.append(.{ .key = text_key, .value = text_expr }); + var child_array = std.ArrayList(Expr).init(self.allocator); + try child_array.append(text_expr); + const children_array = Expr.init(E.Array, .{ .items = .fromList(child_array) }, .Empty); + const children_key = try self.createStringExpr("__children"); + try properties.append(.{ .key = children_key, .value = children_array }); } const element = Expr.init(E.Object, .{ .properties = .fromList(properties) }, .Empty); diff --git a/test/js/bun/xml/xml.test.ts b/test/js/bun/xml/xml.test.ts index 9bf86565d7..fa8de94938 100644 --- a/test/js/bun/xml/xml.test.ts +++ b/test/js/bun/xml/xml.test.ts @@ -3,17 +3,13 @@ import { expect, test } from "bun:test"; test("Bun.XML.parse - simple text element", () => { const xml = "Hello World"; const result = Bun.XML.parse(xml); - expect(result).toEqual({ - __text: "Hello World", - }); + expect(result).toEqual("Hello World"); }); test("Bun.XML.parse - element with whitespace", () => { const xml = " content "; const result = Bun.XML.parse(xml); - expect(result).toEqual({ - __text: " content ", - }); + expect(result).toEqual(" content "); }); test("Bun.XML.parse - empty element", () => { @@ -28,16 +24,14 @@ test("Bun.XML.parse - element with attributes", () => { expect(result).toEqual({ id: "1", type: "info", - __text: "Hello", + __children: ["Hello"], }); }); test("Bun.XML.parse - with XML declaration", () => { const xml = 'content'; const result = Bun.XML.parse(xml); - expect(result).toEqual({ - __text: "content", - }); + expect(result).toEqual("content"); }); test("Bun.XML.parse - empty string", () => { @@ -69,8 +63,8 @@ test("Bun.XML.parse - nested elements", () => { `; const result = Bun.XML.parse(xml); expect(result).toEqual({ - name: { __text: "John" }, - age: { __text: "30" }, + name: "John", + age: "30", }); }); @@ -85,7 +79,7 @@ test("Bun.XML.parse - complex nested structure", () => { name: "John", address: { type: "home", - city: { __text: "New York" }, + city: "New York", }, }); }); @@ -98,24 +92,20 @@ test("Bun.XML.parse - mixed content (text and children)", () => { `; const result = Bun.XML.parse(xml); expect(result).toEqual({ - child: { __text: "value" }, + __children: ["value"], }); }); test("Bun.XML.parse - XML entities", () => { const xml = "Hello <world> & "everyone" 'here'"; const result = Bun.XML.parse(xml); - expect(result).toEqual({ - __text: `Hello & "everyone" 'here'`, - }); + expect(result).toEqual(`Hello & "everyone" 'here'`); }); test("Bun.XML.parse - numeric entities", () => { const xml = "ABC"; const result = Bun.XML.parse(xml); - expect(result).toEqual({ - __text: "ABC", - }); + expect(result).toEqual("ABC"); }); test("Bun.XML.parse - entities in attributes", () => { @@ -123,7 +113,7 @@ test("Bun.XML.parse - entities in attributes", () => { const result = Bun.XML.parse(xml); expect(result).toEqual({ attr: "", - __text: "content", + __children: ["content"], }); }); @@ -135,7 +125,7 @@ test("Bun.XML.parse - XML comments are ignored", () => { `; const result = Bun.XML.parse(xml); expect(result).toEqual({ - message: { __text: "Hello" }, + message: "Hello", }); }); @@ -143,16 +133,14 @@ test("Bun.XML.parse - duplicate tags become arrays", () => { const xml = "12"; const result = Bun.XML.parse(xml); expect(result).toEqual({ - item: [{ __text: "1" }, { __text: "2" }], + item: ["1", "2"], }); }); test("Bun.XML.parse - CDATA sections", () => { const xml = ' & "everyone"]]>'; const result = Bun.XML.parse(xml); - expect(result).toEqual({ - __text: `Hello & "everyone"`, - }); + expect(result).toEqual(`Hello & "everyone"`); }); test("Bun.XML.parse - top-level comments are ignored", () => { @@ -160,9 +148,7 @@ test("Bun.XML.parse - top-level comments are ignored", () => { content `; const result = Bun.XML.parse(xml); - expect(result).toEqual({ - __text: "content", - }); + expect(result).toEqual("content"); }); test("Bun.XML.parse - mismatched closing tag throws error", () => {