01
April 10, 2026 · View on GitHub
Installation
npm install @nodable/flexible-xml-parser @nodable/compact-builder
Install additional output builders only as needed:
npm install @nodable/node-tree-builder
npm install @nodable/sequential-builder
Your First Parser
import XMLParser from '@nodable/flexible-xml-parser';
const parser = new XMLParser();
const result = parser.parse(`
<books>
<book id="1">
<title>The Great Gatsby</title>
<year>1925</year>
<price>10.99</price>
</book>
</books>
`);
// { books: { book: { year: 1925, price: 10.99, title: 'The Great Gatsby' } } }
Numbers and booleans are automatically coerced. To include attributes, disable the default skip:
const parser = new XMLParser({ skip: { attributes: false } });
parser.parse('<book id="1"><title>1984</title></book>');
// { book: { '@_id': 1, title: '1984' } }
Common Patterns
Parse a config file
const parser = new XMLParser();
const config = parser.parse(xmlString);
console.log(config.config.database.host); // 'localhost'
console.log(config.config.database.port); // 5432 (number)
console.log(config.config.cache.enabled); // true (boolean)
Parse an RSS feed
const parser = new XMLParser({ skip: { attributes: false } });
const feed = parser.parse(rssFeedXml);
for (const item of feed.rss.channel.item) {
console.log(item.title, item.link);
}
Keep everything as raw strings
import { CompactBuilderFactory } from '@nodable/compact-builder';
const builder = new CompactBuilderFactory({
tags: { valueParsers: [] },
attributes: { valueParsers: [] },
});
const parser = new XMLParser({ OutputBuilder: builder });
// All values come out as strings — no type coercion
Keep leading zeros (e.g. SKUs, zip codes)
import { CompactBuilderFactory } from '@nodable/compact-builder';
import { NumberValueParser } from '@nodable/base-output-builder';
const builder = new CompactBuilderFactory({
tags: {
valueParsers: ['entity', new NumberValueParser({ leadingZeros: false }), 'boolean'],
},
});
const parser = new XMLParser({ OutputBuilder: builder });
parser.parse('<item><sku>00123</sku><price>9.99</price></item>');
// { item: { sku: '00123', price: 9.99 } }
Strip namespace prefixes
const parser = new XMLParser({ skip: { nsPrefix: true, attributes: false } });
parser.parse('<soap:Envelope><soap:Body><m:Item>Apple</m:Item></soap:Body></soap:Envelope>');
// { Envelope: { Body: { Item: 'Apple' } } }
Parse untrusted XML safely
const parser = new XMLParser({
limits: { maxNestedTags: 50, maxAttributesPerTag: 20 },
doctypeOptions: { enabled: false },
});
try {
const result = parser.parse(untrustedXml);
} catch (e) {
if (e instanceof ParseError) {
console.error(e.code, e.message);
}
}
Handle CDATA
// Option 1 (default): CDATA merged into text
const parser1 = new XMLParser();
parser1.parse('<html><![CDATA[<div>content</div>]]></html>');
// { html: '<div>content</div>' }
// Option 2: separate CDATA key
const parser2 = new XMLParser({ nameFor: { cdata: '#cdata' } });
parser2.parse('<html><![CDATA[<div>content</div>]]></html>');
// { html: { '#cdata': '<div>content</div>' } }
Quick Reference — Most Used Options
new XMLParser({
skip: { attributes: false }, // parse attributes
nameFor: { cdata: '#cdata' }, // separate CDATA key
attributes:{ prefix: '@_' }, // attribute key prefix
tags: { unpaired: ['br', 'img'] }, // void/self-closing HTML tags
limits: { maxNestedTags: 100 }, // DoS guard
});
➡ Next: 02 — Options Reference