A high-performance, pull-based XML parser for JavaScript/TypeScript inspired by Java's StAX (Streaming API for XML). It offers both fully asynchronous, stream-based parsing for large files and synchronous parsing for smaller, in-memory XML documents. Unlike traditional XML-to-JSON mappers, StAX-XML allows you to map XML data to any custom structure you desire while efficiently handling XML files through streaming or direct string processing.
- Declarative Converter API: Zod-style schema API for type-safe XML parsing and writing
- XPath Support: Use XPath expressions for flexible element selection
- Bidirectional Transformation: Parse XML to objects and write objects back to XML
- Fully Asynchronous (Stream-based): For memory-efficient processing of large XML files
- Synchronous (String-based): For high-performance parsing of smaller, in-memory XML strings
- Pull-based Parsing: Stream-based approach for memory-efficient processing of large XML files
- Custom Mapping: Map XML data to any structure you want, not just plain JSON objects
- High Performance: Optimized for speed and low memory usage
- Universal Compatibility: Works in Node.js, Bun, Deno, and web browsers using only Web Standard APIs
- Namespace Support: Basic XML namespace handling
- Entity Support: Built-in entity decoding with custom entity support
- TypeScript Ready: Full TypeScript support with comprehensive type definitions
# npm
npm install stax-xml
# yarn
yarn add stax-xml
# pnpm
pnpm add stax-xml
# bun
bun add stax-xml
# deno
deno add npm:stax-xmlHere are basic examples to get started. StAX-XML provides two parsing approaches:
- Event-based API: Low-level streaming parser for fine-grained control
- Converter API: Declarative, zod-style schema API for type-safe XML parsing
The converter module provides a zod-style declarative API for parsing and writing XML:
import { x } from 'stax-xml/converter';
// Define schema with XPath
const bookSchema = x.object({
title: x.string().xpath('/book/title'),
author: x.string().xpath('/book/author'),
price: x.number().xpath('/book/price'),
tags: x.string().array().xpath('/book/tags/tag')
});
// Parse XML
const xml = `
<book>
<title>TypeScript Deep Dive</title>
<author>John Smith</author>
<price>29.99</price>
<tags>
<tag>programming</tag>
<tag>typescript</tag>
</tags>
</book>
`;
const result = await bookSchema.parse(xml);
// Result: { title: 'TypeScript Deep Dive', author: 'John Smith', price: 29.99, tags: ['programming', 'typescript'] }
// Write XML back
const newXml = await bookSchema.write(result, { rootElement: 'book' });Key features of the Converter API:
- Type-safe parsing: Infer TypeScript types from schemas
- XPath support: Use XPath expressions for element selection
- Bidirectional: Parse XML โ Object and Object โ XML
- Composable: Build complex schemas from simple primitives
- Optional values: Handle missing elements gracefully with
.optional() - Transformations: Apply custom transformations with
.transform()
import { StaxXmlParser, XmlEventType } from 'stax-xml';
const xmlContent = '<root><item>Hello</item></root>';
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode(xmlContent));
controller.close();
}
});
async function parseXml() {
const parser = new StaxXmlParser(stream);
for await (const event of parser) {
console.log(event);
}
}
parseXml();import { StaxXmlParserSync, XmlEventType } from 'stax-xml';
const xmlContent = '<data><value>123</value></data>';
const parser = new StaxXmlParserSync(xmlContent);
for (const event of parser) {
console.log(event);
}For detailed API documentation:
- Converter API Guide: Declarative parsing with schemas
- StaxXmlParser (Asynchronous): Event-based parsing from streams
- StaxXmlParserSync (Synchronous): Event-based parsing from strings
StAX-XML uses only Web Standard APIs, making it compatible with:
- Node.js (v18+)
- Bun (any version)
- Deno (any version)
- Web Browsers (modern browsers)
- Edge Runtime (Vercel, Cloudflare Workers, etc.)
bun testDisclaimer: These benchmarks were performed on a specific system (cpu: 13th Gen Intel(R) Core(TM) i5-13600K, runtime: node 22.17.0 (x64-win32)) and may vary on different hardware and environments.
large.xml (97MB) parsing
| Benchmark | avg (min โฆ max) | p75 / p99 | Memory (avg) |
|---|---|---|---|
| stax-xml to object | 4.36 s/iter | 4.42 s | 2.66 mb |
| stax-xml consume | 3.61 s/iter | 3.65 s | 3.13 mb |
| xml2js | 6.00 s/iter | 6.00 s | 1.80 mb |
| fast-xml-parser | 4.25 s/iter | 4.26 s | 151.81 mb |
| txml | 1.05 s/iter | 1.06 s | 179.81 mb |
midsize.xml (13MB) parsing
| Benchmark | avg (min โฆ max) | p75 / p99 | Memory (avg) |
|---|---|---|---|
| stax-xml to object | 492.06 ms/iter | 493.28 ms | 326.28 kb |
| stax-xml consume | 469.66 ms/iter | 471.54 ms | 174.51 kb |
| xml2js | 163.26 ยตs/iter | 161.20 ยตs | 89.89 kb |
| fast-xml-parser | 529.99 ms/iter | 531.12 ms | 1.92 mb |
| txml | 112.81 ms/iter | 113.26 ms | 1.00 mb |
complex.xml (2KB) parsing
| Benchmark | avg (min โฆ max) | p75 / p99 | Memory (avg) |
|---|---|---|---|
| stax-xml to object | 85.79 ยตs/iter | 75.60 ยตs | 105.11 kb |
| stax-xml consume | 50.38 ยตs/iter | 49.43 ยตs | 271.12 b |
| xml2js | 147.45 ยตs/iter | 153.50 ยตs | 89.42 kb |
| fast-xml-parser | 101.11 ยตs/iter | 102.20 ยตs | 92.92 kb |
| txml | 9.40 ยตs/iter | 9.41 ยตs | 125.89 b |
books.xml (4KB) parsing
| Benchmark | avg (min โฆ max) | p75 / p99 | Memory (avg) |
|---|---|---|---|
| stax-xml to object | 166.73 ยตs/iter | 156.20 ยตs | 221.40 kb |
| stax-xml consume | 176.45 ยตs/iter | 151.70 ยตs | 202.08 kb |
| xml2js | 259.90 ยตs/iter | 254.50 ยตs | 161.25 kb |
| fast-xml-parser | 239.57 ยตs/iter | 203.30 ยตs | 226.17 kb |
| txml | 19.18 ยตs/iter | 19.26 ยตs | 303.13 b |
Sources of sample XML files used in testing:
books.xml: Microsoft XML Document Examplessimple-namespace.xml: W3Schools XML Namespaces Guidetreebank_e.xml: University of Washington XML Data Repository
MIT
Contributions are welcome! Please feel free to submit a Pull Request.
Java์ StAX(Streaming API for XML)์์ ์๊ฐ์ ๋ฐ์ ๊ณ ์ฑ๋ฅ pull ๋ฐฉ์์ JavaScript/TypeScript XML ํ์์ ๋๋ค. ๋์ฉ๋ ํ์ผ์ ์ํ ์์ ๋น๋๊ธฐ ์คํธ๋ฆผ ๊ธฐ๋ฐ ํ์ฑ๊ณผ ์์ ์ธ๋ฉ๋ชจ๋ฆฌ XML ๋ฌธ์๋ฅผ ์ํ ๋๊ธฐ ํ์ฑ์ ๋ชจ๋ ์ ๊ณตํฉ๋๋ค. ๊ธฐ์กด์ XML-JSON ๋งคํผ์ ๋ฌ๋ฆฌ, StAX-XML์ ์ฌ์ฉํ๋ฉด XML ๋ฐ์ดํฐ๋ฅผ ์ํ๋ ์์์ ๊ตฌ์กฐ๋ก ๋งคํํ ์ ์์ผ๋ฉฐ, ์คํธ๋ฆฌ๋ฐ ๋๋ ์ง์ ๋ฌธ์์ด ์ฒ๋ฆฌ๋ฅผ ํตํด XML ํ์ผ์ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
- ์ ์ธ์ Converter API: ํ์ ์์ ํ XML ํ์ฑ๊ณผ ์ฐ๊ธฐ๋ฅผ ์ํ Zod ์คํ์ผ ์คํค๋ง API
- XPath ์ง์: ์ ์ฐํ ์์ ์ ํ์ ์ํ XPath ํํ์ ์ฌ์ฉ
- ์๋ฐฉํฅ ๋ณํ: XML์ ๊ฐ์ฒด๋ก ํ์ฑํ๊ณ ๊ฐ์ฒด๋ฅผ ๋ค์ XML๋ก ์์ฑ
- ์์ ๋น๋๊ธฐ (์คํธ๋ฆผ ๊ธฐ๋ฐ): ๋์ฉ๋ XML ํ์ผ์ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ฒ๋ฆฌ
- ๋๊ธฐ (๋ฌธ์์ด ๊ธฐ๋ฐ): ์์ ์ธ๋ฉ๋ชจ๋ฆฌ XML ๋ฌธ์์ด์ ๊ณ ์ฑ๋ฅ ํ์ฑ
- ์ฌ์ฉ์ ์ ์ ๋งคํ: ๋จ์ํ JSON ๊ฐ์ฒด๊ฐ ์๋ ์ํ๋ ๊ตฌ์กฐ๋ก XML ๋ฐ์ดํฐ ๋งคํ ๊ฐ๋ฅ
- ๊ณ ์ฑ๋ฅ: ์๋์ ๋ฎ์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ต์ ํ
- ๋ฒ์ฉ ํธํ์ฑ: ์น ํ์ค API๋ง ์ฌ์ฉํ์ฌ Node.js, Bun, Deno, ์น ๋ธ๋ผ์ฐ์ ์์ ๋ชจ๋ ๋์
- ๋ค์์คํ์ด์ค ์ง์: ๊ธฐ๋ณธ XML ๋ค์์คํ์ด์ค ์ฒ๋ฆฌ
- ์ํฐํฐ ์ง์: ์ฌ์ฉ์ ์ ์ ์ํฐํฐ ์ง์์ ํฌํจํ ๋ด์ฅ ์ํฐํฐ ๋์ฝ๋ฉ
- TypeScript ์ง์: ํฌ๊ด์ ์ธ ํ์ ์ ์๋ก ์์ ํ TypeScript ์ง์
# npm
npm install stax-xml
# yarn
yarn add stax-xml
# pnpm
pnpm add stax-xml
# bun
bun add stax-xml
# deno
deno add npm:stax-xml์์ธํ ์ฌ์ฉ๋ฒ, API ์ฐธ์กฐ, ํํ ๋ฆฌ์ผ์ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ธ์.
StAX-XML์ ๋ ๊ฐ์ง ํ์ฑ ๋ฐฉ์์ ์ ๊ณตํฉ๋๋ค:
- ์ด๋ฒคํธ ๊ธฐ๋ฐ API: ์ธ๋ฐํ ์ ์ด๋ฅผ ์ํ ์ ์์ค ์คํธ๋ฆฌ๋ฐ ํ์
- Converter API: ํ์ ์์ ํ XML ํ์ฑ์ ์ํ ์ ์ธ์ Zod ์คํ์ผ ์คํค๋ง API
Converter ๋ชจ๋์ XML ํ์ฑ ๋ฐ ์ฐ๊ธฐ๋ฅผ ์ํ Zod ์คํ์ผ์ ์ ์ธ์ API๋ฅผ ์ ๊ณตํฉ๋๋ค:
import { x } from 'stax-xml/converter';
// XPath๋ฅผ ์ฌ์ฉํ ์คํค๋ง ์ ์
const bookSchema = x.object({
title: x.string().xpath('/book/title'),
author: x.string().xpath('/book/author'),
price: x.number().xpath('/book/price'),
tags: x.string().array().xpath('/book/tags/tag')
});
// XML ํ์ฑ
const xml = `
<book>
<title>TypeScript ๋ฅ๋ค์ด๋ธ</title>
<author>ํ๊ธธ๋</author>
<price>29.99</price>
<tags>
<tag>ํ๋ก๊ทธ๋๋ฐ</tag>
<tag>ํ์
์คํฌ๋ฆฝํธ</tag>
</tags>
</book>
`;
const result = await bookSchema.parse(xml);
// ๊ฒฐ๊ณผ: { title: 'TypeScript ๋ฅ๋ค์ด๋ธ', author: 'ํ๊ธธ๋', price: 29.99, tags: ['ํ๋ก๊ทธ๋๋ฐ', 'ํ์
์คํฌ๋ฆฝํธ'] }
// XML๋ก ๋ค์ ์ฐ๊ธฐ
const newXml = await bookSchema.write(result, { rootElement: 'book' });Converter API์ ์ฃผ์ ๊ธฐ๋ฅ:
- ํ์ ์์ ํ์ฑ: ์คํค๋ง์์ TypeScript ํ์ ์๋ ์ถ๋ก
- XPath ์ง์: ์์ ์ ํ์ ์ํ XPath ํํ์ ์ฌ์ฉ
- ์๋ฐฉํฅ: XML โ ๊ฐ์ฒด, ๊ฐ์ฒด โ XML ๋ณํ
- ์กฐํฉ ๊ฐ๋ฅ: ๋จ์ ๊ธฐ๋ณธํ์์ ๋ณต์กํ ์คํค๋ง ๊ตฌ์ถ
- ์ ํ์ ๊ฐ:
.optional()๋ก ๋๋ฝ๋ ์์ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌ - ๋ณํ:
.transform()์ผ๋ก ์ฌ์ฉ์ ์ ์ ๋ณํ ์ ์ฉ
import { StaxXmlParser, XmlEventType } from 'stax-xml';
const xmlContent = '<root><item>์๋
ํ์ธ์</item></root>';
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode(xmlContent));
controller.close();
}
});
async function parseXml() {
const parser = new StaxXmlParser(stream);
for await (const event of parser) {
console.log(event);
}
}
parseXml();import { StaxXmlParserSync, XmlEventType } from 'stax-xml';
const xmlContent = '<data><value>123</value></data>';
const parser = new StaxXmlParserSync(xmlContent);
for (const event of parser) {
console.log(event);
}์์ธํ API ๋ฌธ์๋ ๋ค์์ ์ฐธ์กฐํ์ธ์:
- Converter API ๊ฐ์ด๋: ์คํค๋ง๋ฅผ ์ฌ์ฉํ ์ ์ธ์ ํ์ฑ
- StaxXmlParser (๋น๋๊ธฐ): ์คํธ๋ฆผ ๊ธฐ๋ฐ ์ด๋ฒคํธ ํ์ฑ
- StaxXmlParserSync (๋๊ธฐ): ๋ฌธ์์ด ๊ธฐ๋ฐ ์ด๋ฒคํธ ํ์ฑ
StAX-XML์ ์น ํ์ค API๋ง์ ์ฌ์ฉํ์ฌ ๋ค์ ํ๊ฒฝ์์ ๋์ํฉ๋๋ค:
- Node.js (v18+)
- Bun (๋ชจ๋ ๋ฒ์ )
- Deno (๋ชจ๋ ๋ฒ์ )
- ์น ๋ธ๋ผ์ฐ์ (์ต์ ๋ธ๋ผ์ฐ์ )
- Edge Runtime (Vercel, Cloudflare Workers ๋ฑ)
ํ ์คํธ์ ์ฌ์ฉ๋ ์ํ ํ์ผ๋ค์ ์ถ์ฒ:
XML ํ์ผ:
books.xml: Microsoft XML ๋ฌธ์ ์์ simple-namespace.xml: W3Schools XML ๋ค์์คํ์ด์ค ๊ฐ์ด๋treebank_e.xml: University of Washington XML Data Repository
JSON ํ์ผ:
MIT
๊ธฐ์ฌ๋ฅผ ํ์ํฉ๋๋ค! Pull Request๋ฅผ ์์ ๋กญ๊ฒ ์ ์ถํด ์ฃผ์ธ์.