StAX-XML: Hochleistungs-Streaming-XML-Parser für JavaScript/TypeScript
(github.com/Clickin)Da ich auf der Arbeit mit einem in Java geschriebenen Legacy-Service arbeite, der XML-basiert kommuniziert, und wir bei einem neuen Webservice den Legacy-Service als Backend beibehalten, aber auf JS-Basis neu entwickeln wollten, gab es keinen XML-Parser, der mir wirklich gefiel — also habe ich selbst einen gebaut.
Er parst XML mit einem StAX-basierten Pull-Ansatz und bietet eine asynchrone Implementierung, sodass sich auch große XML-Dateien auf Stream-Basis mit nur etwa 10 MB Speicher parsen lassen.
Da die maximale Länge von string im ECMAScript-Standard bei 2^53 - 1 liegt, blieb man bei XML-Dateien über 1 GB bisher praktisch auf SAX-Parser angewiesen — ich denke, diese Bibliothek kann dafür eine gute Alternative sein.
Da ich beruflich hauptsächlich Java nutze und dies mein erstes Mal ist, eine Bibliothek für das Node-Ökosystem zu entwickeln, freue ich mich über Vorschläge zu Schwachstellen und werde sie nach Möglichkeit einarbeiten.
Geschichte
Zuerst habe ich überlegt, die Java-Bibliothek woodstox per WASM-Binding zu verwenden,
aber damals gab es in WASM noch keine GC-Implementierung, daher hielt ich das Kompilieren von Java nach WASM noch für verfrüht und habe es gelassen.
Als Zweites habe ich versucht, Rusts quick-xml per WASM-Binding einzusetzen, aber die Kosten dafür, Streams an WASM zu übergeben und dort zu verarbeiten, waren zu hoch, sodass der Performance-Unterschied zu bestehenden JavaScript-XML-Parsern viel zu groß war — deshalb habe ich auch das verworfen.
Letztlich habe ich mich entschieden, alles in reinem TypeScript zu schreiben, und mit Unterstützung mehrerer AIs verschiedene Optimierungen speziell für die V8-Engine angewendet.
🚀 Hauptmerkmale
Vollständig asynchrones, streambasiertes Parsing
- Große XML-Dateien (mehrere hundert MB bis GB) speichereffizient verarbeiten
- Auf Basis von ReadableStream für Echtzeit-Parsing ohne Blockieren des Main Threads
- Pull-Ansatz zur Verarbeitung nur der tatsächlich benötigten Daten
// Selbst eine 970MB-Datei lässt sich mit unter 10MB Speicher verarbeiten
const parser = new StaxXmlParser(largeXmlStream);
for await (const event of parser) {
// Ereignisse per Streaming verarbeiten
}
Ereignisbasiertes Parsing im StAX-Stil
Bietet Java-Entwicklern ein vertrautes Pull-Parser-Muster, mit dem sich die XML-Struktur fein granular steuern lässt.
import { StaxXmlParser, isStartElement, isCharacters } from 'stax-xml';
for await (const event of parser) {
if (isStartElement(event)) {
console.log(`Element: ${event.name}`, event.attributes);
} else if (isCharacters(event)) {
console.log(`Text: ${event.value}`);
}
}
🛠️ Vier Kernkomponenten
1. StaxXmlParser (asynchroner Parser)
- Für große Dateien: streambasiertes, speichereffizientes Parsing
- Echtzeitverarbeitung: Remote-XML in Echtzeit über die Fetch API parsen
- TypeScript-Type-Guards: garantierte Typensicherheit zur Laufzeit
2. StaxXmlParserSync (synchroner Parser)
- Für kleine Dateien optimiert: schnelles Parsing von XML-Strings im Speicher
- Web-API-Antworten: sofortige Verarbeitung in synchronen Workflows
3. StaxXmlWriter (asynchroner Writer)
- Streaming-Erzeugung: XML direkt in einen WritableStream ausgeben
- Echtzeitantworten: große XML-Antworten auf API-Servern erzeugen
- Speichereffizient: speichert nicht das gesamte XML im Speicher
4. StaxXmlWriterSync (synchroner Writer)
- Sofortige Erzeugung: XML-Strings im Speicher aufbauen
- Webserver-Integration: perfekte Anbindung an Express, Hono usw.
📊 Leistungsvergleich (Benchmark)
Benchmark-Umgebung
- CPU: 13th Gen Intel(R) Core(TM) i5-13600K (~4.70-4.80 GHz)
- Runtime: Node.js 22.17.0 (x64-win32) with --expose-gc
- Tool: Mitata
Parsing einer großen 97MB-Datei:
- stax-xml: 1.05s, Speicher 8.89MB
- fast-xml-parser: 4.41s, Speicher 886.33MB
- txml: 1.02s, Speicher 897.50MB
🌐 Universelle Kompatibilität
Verwendet nur Web-Standard-APIs und läuft dadurch in allen JavaScript-Runtimes:
- Node.js (v18+)
- Bun, Deno
- Webbrowser
- Edge Runtime (Vercel, Cloudflare Workers)
📦 Installation und Einstieg
npm install stax-xml
import { StaxXmlParser, XmlEventType } from 'stax-xml';
// Stream aus einem XML-String erzeugen
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode(xmlContent));
controller.close();
}
});
// Asynchron parsen
const parser = new StaxXmlParser(stream);
for await (const event of parser) {
if (event.type === XmlEventType.START_ELEMENT) {
console.log(`Element: ${event.name}`, event.attributes);
}
}
📄 Projektinformationen
- GitHub: https://github.com/clickin/stax-xml
- Dokumentation: https://clickin.github.io/stax-xml
- NPM: https://www.npmjs.com/package/stax-xml
- Lizenz: MIT
※ Hinweis zur Lizenz: Diese Bibliothek ist von dem in JSR 173: Streaming API for XML vorgeschlagenen StAX-Konzept inspiriert, die Lizenzbedingungen des JSR selbst konnte ich jedoch nicht eindeutig nachvollziehen. Falls jemand die Lizenzklauseln zur Bezeichnung StAX kennt, wäre ich für Hinweise dankbar.
1 Kommentare
Man spürt die Sorgfalt und Hingabe im Artikel, das fand ich sehr schön.
Ich habe ihn gerne gelesen!