StAX-XML: parser XML de streaming de alto rendimiento para JavaScript/TypeScript
(github.com/Clickin)En mi trabajo usamos servicios legacy hechos en Java que se comunican sobre XML, y al intentar crear un nuevo servicio web basado en JS manteniendo esos servicios legacy como backend, no encontré un parser XML que realmente me convenciera, así que terminé haciéndolo yo mismo.
Parsea XML con un enfoque pull basado en StAX y ofrece una implementación asíncrona, por lo que incluso archivos XML grandes basados en streams pueden procesarse con apenas unos 10 MB de memoria.
Como en el estándar ECMAScript la longitud máxima de un string es 2^53 - 1, para XML de más de 1 GB prácticamente no quedaba otra que usar un parser SAX, pero creo que esta librería puede ser una buena alternativa.
Como en el trabajo uso principalmente Java y esta es mi primera vez creando una librería para el ecosistema Node, si ven puntos a mejorar o quieren proponer algo, intentaré reflejarlo lo mejor posible.
Historia
Al principio pensé en enlazar la librería woodstox de Java con WASM,
pero en ese momento WASM todavía no tenía implementación de GC, así que consideré que aún era demasiado pronto para compilar Java a WASM y desistí.
Luego intenté hacerlo enlazando quick-xml de Rust con WASM, pero el costo de pasar streams a WASM para procesarlos era demasiado alto, y la diferencia de rendimiento frente a los parsers XML existentes en JavaScript terminaba siendo demasiado grande, así que también lo descarté.
Al final decidí escribirlo en TypeScript puro y, con ayuda de varias IA, apliqué varias optimizaciones apuntando al motor V8.
🚀 Características principales
Parsing completamente asíncrono y basado en streams
- Archivos XML grandes (cientos de MB a GB) con procesamiento eficiente en memoria
- Parsing en tiempo real basado en ReadableStream sin bloquear el hilo principal
- Procesamiento con enfoque pull, manejando solo los datos necesarios
// Incluso un archivo de 970MB puede procesarse con menos de 10MB de memoria
const parser = new StaxXmlParser(largeXmlStream);
for await (const event of parser) {
// Procesar eventos en streaming
}
Parsing basado en eventos al estilo StAX
Ofrece un patrón de parser pull familiar para desarrolladores Java, permitiendo un control detallado sobre la estructura XML.
import { StaxXmlParser, isStartElement, isCharacters } from 'stax-xml';
for await (const event of parser) {
if (isStartElement(event)) {
console.log(`Elemento: ${event.name}`, event.attributes);
} else if (isCharacters(event)) {
console.log(`Texto: ${event.value}`);
}
}
🛠️ 4 componentes clave
1. StaxXmlParser (parser asíncrono)
- Especializado en archivos grandes: parsing eficiente en memoria basado en streams
- Procesamiento en tiempo real: parsing de XML remoto en tiempo real integrándose con la API
fetch - Type guards de TypeScript: seguridad de tipos en tiempo de ejecución
2. StaxXmlParserSync (parser síncrono)
- Optimizado para archivos pequeños: parsing rápido de strings XML en memoria
- Respuestas de APIs web: procesamiento inmediato en flujos de trabajo síncronos
3. StaxXmlWriter (writer asíncrono)
- Generación en streaming: salida XML directa a
WritableStream - Respuesta en tiempo real: generación de respuestas XML grandes en servidores API
- Eficiente en memoria: no guarda el XML completo en memoria
4. StaxXmlWriterSync (writer síncrono)
- Generación inmediata: construcción de strings XML en memoria
- Integración con servidores web: integración perfecta con Express, Hono, etc.
📊 Comparación de rendimiento (benchmark)
Entorno del benchmark
- 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 de archivo grande de 97MB:
- stax-xml: 1.05s, memoria 8.89MB
- fast-xml-parser: 4.41s, memoria 886.33MB
- txml: 1.02s, memoria 897.50MB
🌐 Compatibilidad universal
Funciona en todos los runtimes de JavaScript usando solo APIs estándar de la web:
- Node.js (v18+)
- Bun, Deno
- Navegadores web
- Edge Runtime (Vercel, Cloudflare Workers)
📦 Instalación e inicio
npm install stax-xml
import { StaxXmlParser, XmlEventType } from 'stax-xml';
// Crear un stream a partir de un string XML
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode(xmlContent));
controller.close();
}
});
// Parsing asíncrono
const parser = new StaxXmlParser(stream);
for await (const event of parser) {
if (event.type === XmlEventType.START_ELEMENT) {
console.log(`Elemento: ${event.name}`, event.attributes);
}
}
📄 Información del proyecto
- GitHub: https://github.com/clickin/stax-xml
- Documentación: https://clickin.github.io/stax-xml
- NPM: https://www.npmjs.com/package/stax-xml
- Licencia: MIT
※ Nota sobre la licencia: esta librería se inspiró en el concepto de StAX propuesto en JSR 173: Streaming API for XML, pero no pude identificar con claridad las condiciones de licencia del propio JSR. Si alguien conoce las cláusulas de licencia del nombre StAX, agradecería cualquier orientación.
1 comentarios
Se nota el cuidado y la dedicación en el artículo, y eso me gustó mucho.
¡Lo leí muy a gusto!