Skip to content

Commit 94a6166

Browse files
committed
Don't automatically convert types when parsing XML
1 parent d5aee1e commit 94a6166

File tree

2 files changed

+139
-5
lines changed

2 files changed

+139
-5
lines changed

src/utils/__tests__/xml.test.ts

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { parseXml } from "../xml"
2+
3+
describe("parseXml", () => {
4+
describe("type conversion", () => {
5+
// Test the main change from the commit: no automatic type conversion
6+
it("should not convert string numbers to numbers", () => {
7+
const xml = `
8+
<root>
9+
<numericString>123</numericString>
10+
<negativeNumericString>-456</negativeNumericString>
11+
<floatNumericString>123.456</floatNumericString>
12+
</root>
13+
`
14+
15+
const result = parseXml(xml) as any
16+
17+
// Ensure these remain as strings and are not converted to numbers
18+
expect(typeof result.root.numericString).toBe("string")
19+
expect(result.root.numericString).toBe("123")
20+
21+
expect(typeof result.root.negativeNumericString).toBe("string")
22+
expect(result.root.negativeNumericString).toBe("-456")
23+
24+
expect(typeof result.root.floatNumericString).toBe("string")
25+
expect(result.root.floatNumericString).toBe("123.456")
26+
})
27+
28+
it("should not convert string booleans to booleans", () => {
29+
const xml = `
30+
<root>
31+
<boolTrue>true</boolTrue>
32+
<boolFalse>false</boolFalse>
33+
</root>
34+
`
35+
36+
const result = parseXml(xml) as any
37+
38+
// Ensure these remain as strings and are not converted to booleans
39+
expect(typeof result.root.boolTrue).toBe("string")
40+
expect(result.root.boolTrue).toBe("true")
41+
42+
expect(typeof result.root.boolFalse).toBe("string")
43+
expect(result.root.boolFalse).toBe("false")
44+
})
45+
46+
it("should not convert attribute values to their respective types", () => {
47+
const xml = `
48+
<root>
49+
<node id="123" enabled="true" disabled="false" float="3.14" />
50+
</root>
51+
`
52+
53+
const result = parseXml(xml) as any
54+
const attributes = result.root.node
55+
56+
// Check that attributes remain as strings
57+
expect(typeof attributes["@_id"]).toBe("string")
58+
expect(attributes["@_id"]).toBe("123")
59+
60+
expect(typeof attributes["@_enabled"]).toBe("string")
61+
expect(attributes["@_enabled"]).toBe("true")
62+
63+
expect(typeof attributes["@_disabled"]).toBe("string")
64+
expect(attributes["@_disabled"]).toBe("false")
65+
66+
expect(typeof attributes["@_float"]).toBe("string")
67+
expect(attributes["@_float"]).toBe("3.14")
68+
})
69+
})
70+
71+
describe("basic functionality", () => {
72+
it("should correctly parse a simple XML string", () => {
73+
const xml = `
74+
<root>
75+
<name>Test Name</name>
76+
<description>Some description</description>
77+
</root>
78+
`
79+
80+
const result = parseXml(xml) as any
81+
82+
expect(result).toHaveProperty("root")
83+
expect(result.root).toHaveProperty("name", "Test Name")
84+
expect(result.root).toHaveProperty("description", "Some description")
85+
})
86+
87+
it("should handle attributes correctly", () => {
88+
const xml = `
89+
<root>
90+
<item id="1" category="test">Item content</item>
91+
</root>
92+
`
93+
94+
const result = parseXml(xml) as any
95+
96+
expect(result.root.item).toHaveProperty("@_id", "1")
97+
expect(result.root.item).toHaveProperty("@_category", "test")
98+
expect(result.root.item).toHaveProperty("#text", "Item content")
99+
})
100+
101+
it("should support stopNodes parameter", () => {
102+
const xml = `
103+
<root>
104+
<data>
105+
<nestedXml><item>Should not parse this</item></nestedXml>
106+
</data>
107+
</root>
108+
`
109+
110+
const result = parseXml(xml, ["nestedXml"]) as any
111+
112+
// With stopNodes, the parser still parses the structure but stops at the specified node
113+
expect(result.root.data.nestedXml).toBeTruthy()
114+
expect(result.root.data.nestedXml).toHaveProperty("item", "Should not parse this")
115+
})
116+
})
117+
118+
describe("error handling", () => {
119+
it("wraps parser errors with a descriptive message", () => {
120+
// Mock XMLParser to simulate a parsing error
121+
const originalParser = require("fast-xml-parser").XMLParser
122+
require("fast-xml-parser").XMLParser = jest.fn().mockImplementation(() => {
123+
return {
124+
parse: () => {
125+
throw new Error("Simulated parsing error")
126+
},
127+
}
128+
})
129+
130+
// Test that our function wraps the error appropriately
131+
expect(() => parseXml("<root></root>")).toThrow("Failed to parse XML: Simulated parsing error")
132+
133+
// Restore the original parser
134+
require("fast-xml-parser").XMLParser = originalParser
135+
})
136+
})
137+
})

src/utils/xml.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,10 @@ export function parseXml(xmlString: string, stopNodes?: string[]): unknown {
1010
const _stopNodes = stopNodes ?? []
1111
try {
1212
const parser = new XMLParser({
13-
// Preserve attribute types (don't convert numbers/booleans)
1413
ignoreAttributes: false,
1514
attributeNamePrefix: "@_",
16-
// Parse numbers and booleans in text nodes
17-
parseAttributeValue: true,
18-
parseTagValue: true,
19-
// Trim whitespace from text nodes
15+
parseAttributeValue: false,
16+
parseTagValue: false,
2017
trimValues: true,
2118
stopNodes: _stopNodes,
2219
})

0 commit comments

Comments
 (0)