Bun.XML.parse() basic usage
Bun.XML.parse() parses an XML document into a plain JavaScript object. It accepts a string or bytes as Buffer, TypedArray, ArrayBuffer, or Blob. By default it returns a compact object keyed by element name, where the root element becomes a single key, elements with no attributes and no children become their text content (trimmed whitespace, empty string when empty), other elements become objects with '@name' keys per attribute, one key per distinct child element name (an array when that name repeats), and '#text' for trimmed character data if any. CDATA sections and entity references are expanded, comments and processing instructions are dropped, and all values are strings.
Bun.XML.parse() compact vs node tree modes
By default Bun.XML.parse() returns a compact object that does not preserve relative order of differently named siblings or text between child elements. Pass { compact: false } to get a node tree that preserves document order. In node tree mode, every element is { name, attributes, children } where children holds child elements and strings, and text is passed through exactly including whitespace-only runs between elements.
Bun.XML.parse() encoding handling
A string input to XML.parse has its encoding declaration checked for syntax but otherwise ignored. Bytes are decoded per XML rules: a byte-order mark or the encoding attribute in <?xml version="1.0" encoding="..."?> selects UTF-8 (default), UTF-16 (either byte order), or ISO-8859-1. Other encodings throw an error.
Bun.XML.parse() error handling
Bun.XML.parse() throws a SyntaxError when the document is not well-formed. The error message describes what was found wrong, such as 'XML Parse error: Expected closing tag </b> but found </a>'.
Bun.XML.parse() compact object example
Example of Bun.XML.parse() output in compact format:
```ts
const data = XML.parse(`
<order id="A1" currency="USD">
<customer>Ada</customer>
<item sku="tea" qty="2">Green tea</item>
<item sku="mug" qty="1">Mug</item>
<paid/>
</order>
`);
// Returns:
// {
// order: {
// "@id": "A1",
// "@currency": "USD",
// customer: "Ada",
// item: [
// { "@sku": "tea", "@qty": "2", "#text": "Green tea" },
// { "@sku": "mug", "@qty": "1", "#text": "Mug" },
// ],
// paid: "",
// },
// }
```
Bun.XML.parse() node tree example
Example of Bun.XML.parse() with { compact: false }:
```ts
const p = XML.parse(`<p class="lead">Hello <b>world</b>!</p>`, { compact: false });
// Returns:
// {
// name: "p",
// attributes: { class: "lead" },
// children: [
// "Hello ",
// { name: "b", attributes: {}, children: ["world"] },
// "!",
// ],
// }
```
Bun.XML.stringify() basic usage
Bun.XML.stringify() serializes either compact or node tree shape back to XML. The output has no XML declaration and is always well-formed with &, <, > (and in attributes, quotes, tabs and newlines) escaped. Element or attribute names that are not XML names throw an error. A value with string name and children or attributes property is written as a node; anything else is a compact object and must have exactly one key naming the root element.
Bun.XML.stringify() value handling
In Bun.XML.stringify(), strings, numbers, booleans, bigints and Dates (as ISO strings) become text. null becomes an empty element. undefined, functions and symbols are skipped like JSON.stringify skips them. Unlike JSON.stringify, a bigint is written as its decimal digits rather than rejected.
Bun.XML.stringify() pretty printing
Pass a space argument to Bun.XML.stringify() (a number of spaces or indent string, as with JSON.stringify) to indent element-only content. Elements that contain text are written inline so character data is unchanged. Example: XML.stringify(data, null, 2) indents with 2 spaces.
Bun.XML.stringify() example
Example of Bun.XML.stringify():
```ts
XML.stringify({
order: {
"@id": "A1",
customer: "Ada",
item: [{ "@sku": "tea", "#text": "Green tea" }, { "@sku": "mug" }],
paid: null,
},
});
// Returns: '<order id="A1"><customer>Ada</customer><item sku="tea">Green tea</item><item sku="mug"/><paid/></order>'
XML.stringify({
name: "p",
attributes: { class: "lead" },
children: ["Hello ", { name: "b", children: ["world"] }, "!"],
});
// Returns: '<p class="lead">Hello <b>world</b>!</p>'
```
XML file ES module default import
XML files can be imported as ES modules. Files are decoded like bytes passed to XML.parse (UTF-8, UTF-16, or ISO-8859-1), and the module's value is the compact object. Example: import doc from './config.xml' allows accessing doc.config['@env'].
XML file ES module named import
The root element of an XML file is also available as a named import. Example: import { config } from './config.xml' allows accessing config.database['@port'].
XML file CommonJS require
XML files can be required in CommonJS. Example: const { config } = require('./config.xml') allows accessing config.database['@name'].
XML import attributes for non-standard extensions
Use import attributes with { type: 'xml' } to parse a file with a non-standard extension as XML. Example: import feed from './export.rss' with { type: 'xml' }
XML hot reloading with bun --hot
When you run your application with 'bun --hot', Bun reloads XML files when they change, allowing configuration and data files to be updated without restarting the server.
XML bundler integration and build-time parsing
When bundling with Bun, imported XML files are parsed at build time and inlined as JavaScript objects. This provides zero runtime XML parsing overhead in production, smaller bundle sizes, and tree shaking of unused properties.
XML dynamic imports
XML files can be dynamically imported using: const { default: doc } = await import('./config.xml')
Bun.XML conformance to XML 1.0
Bun's XML parser implements XML 1.0 (Fifth Edition) as a non-validating processor that does not read external entities. The whole document including the internal DTD subset must be well-formed. Internal entities are expanded with expansion limits to prevent billion laughs attacks. Attribute values are normalized and attribute defaults from the internal subset are applied. External DTDs and entities are never fetched, preventing XXE attacks. Nothing is validated against the DTD, namespaces are not resolved (prefixed names kept verbatim), and comments and processing instructions are skipped.
Bun.XML undeclared entity handling
In a document with no DTD, a reference to an undeclared entity is an error. When the DOCTYPE points at an external subset that could have declared it, the reference is kept as written (like stays ) unless the document declares standalone="yes".
Bun.XML W3C conformance test results
Bun.XML parser passes all 1,679 cases from the W3C XML Conformance Test Suite that have a required outcome for this class of processor. Not-well-formed documents are rejected, well-formed ones are accepted, and their element tree matches the canonical output byte for byte where the suite specifies one.
Bun.XML parser performance characteristics
Bun.XML parser works in two SIMD stages like Bun's JSON parser: a SIMD pass (runtime-dispatched AVX2/AVX-512/NEON/SVE kernels) finds bytes that can change the parse so character data, attribute values, comments and CDATA sections are never scanned byte-at-a-time. Element and attribute names reuse JavaScriptCore's atom-string cache like JSON.parse does.
Bun.XML performance benchmarks
Performance comparison on Linux x64, one core (lower is better):
- S3 ListObjectsV2 response (231 KB): Bun.XML.parse 1.1ms vs txml 4.0ms vs fast-xml-parser 23ms vs @xmldom/xmldom 31ms vs xml2js 19ms
- Atom feed (193 KB): Bun.XML.parse 1.1ms vs txml 3.7ms vs fast-xml-parser 19ms vs @xmldom/xmldom 23ms vs xml2js 16ms
- libphonenumber metadata (960 KB): Bun.XML.parse 5.3ms vs txml 9.6ms vs fast-xml-parser 56ms vs @xmldom/xmldom 53ms
- Chromium enums.xml (1.4 MB): Bun.XML.parse 16ms vs txml 41ms vs fast-xml-parser 150ms vs @xmldom/xmldom 103ms
- freedesktop MIME database (2.2 MB): Bun.XML.parse 27ms vs txml 56ms vs fast-xml-parser 299ms vs @xmldom/xmldom 280ms
Bun.XML available in version 1.4+
Bun.XML APIs are new in Bun v1.4.