StAX-XML: parser XML de streaming de alto desempenho para JavaScript/TypeScript
(github.com/Clickin)No trabalho, usamos serviços legados em Java que se comunicam com base em XML. Ao tentar criar um novo serviço web baseado em JS mantendo o serviço legado como backend, não encontrei um parser XML que realmente me agradasse, então acabei criando um por conta própria.
Ele faz o parsing de XML no estilo pull baseado em StAX e oferece uma implementação assíncrona, o que permite processar até arquivos XML muito grandes baseados em stream usando cerca de apenas 10 MB de memória.
Como o comprimento máximo de uma string no padrão ecmascript é 2^53 - 1, para XMLs com mais de 1 GB eu acabava tendo que usar um parser SAX, mas acredito que esta biblioteca possa ser uma boa alternativa.
Como no trabalho eu uso Java principalmente e esta é a primeira vez que crio uma biblioteca para o ecossistema Node, se houver pontos a melhorar, fiquem à vontade para sugerir que tentarei refletir isso ao máximo.
História
No começo, pensei em usar a biblioteca woodstox do Java com binding para wasm,
mas naquela época ainda não havia implementação de GC em wasm, então concluí que ainda era cedo para compilar Java para wasm e desisti da ideia.
Depois, tentei usar o quick-xml do Rust com binding para wasm, mas o custo de passar o stream para o wasm para processamento era alto demais, e a diferença de desempenho em relação aos parsers XML em JavaScript já existentes ficou grande demais, então abandonei essa abordagem.
Por fim, decidi escrever tudo em TypeScript puro e, com a ajuda de várias IAs, também apliquei diversas otimizações voltadas ao engine V8.
🚀 Principais características
Parsing totalmente assíncrono e baseado em stream
- Arquivos XML grandes (centenas de MB até GB) com processamento eficiente em memória
- Parsing em tempo real com base em ReadableStream, sem bloquear a thread principal
- Processamento de dados no estilo pull, consumindo apenas o necessário
// Até um arquivo de 970MB pode ser processado com menos de 10MB de memória
const parser = new StaxXmlParser(largeXmlStream);
for await (const event of parser) {
// Processa eventos em streaming
}
Parsing orientado a eventos no estilo StAX
Oferece um padrão de parser pull familiar para desenvolvedores Java, permitindo controle detalhado da estrutura 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 principais
1. StaxXmlParser (parser assíncrono)
- Voltado para arquivos grandes: parsing eficiente em memória com base em stream
- Processamento em tempo real: parsing em tempo real de XML remoto com integração à fetch API
- Type guards de TypeScript: garante segurança de tipos em runtime
2. StaxXmlParserSync (parser síncrono)
- Otimizado para arquivos pequenos: parsing rápido de strings XML em memória
- Respostas de API web: processamento imediato em fluxos de trabalho síncronos
3. StaxXmlWriter (writer assíncrono)
- Geração em streaming: saída XML direta para WritableStream
- Resposta em tempo real: geração de grandes respostas XML em servidores de API
- Eficiente em memória: não armazena o XML inteiro na memória
4. StaxXmlWriterSync (writer síncrono)
- Geração imediata: construção de strings XML em memória
- Integração com servidores web: integração perfeita com Express, Hono etc.
📊 Comparação de desempenho (benchmark)
Ambiente de 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 arquivo grande de 97MB:
- stax-xml: 1.05s, memória 8.89MB
- fast-xml-parser: 4.41s, memória 886.33MB
- txml: 1.02s, memória 897.50MB
🌐 Compatibilidade universal
Usa apenas APIs padrão da web, funcionando em qualquer runtime JavaScript:
- Node.js (v18+)
- Bun, Deno
- Navegadores web
- Edge Runtime (Vercel, Cloudflare Workers)
📦 Instalação e início
npm install stax-xml
import { StaxXmlParser, XmlEventType } from 'stax-xml';
// Cria um stream a partir de uma string XML
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode(xmlContent));
controller.close();
}
});
// Parsing assí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);
}
}
📄 Informações do projeto
- GitHub: https://github.com/clickin/stax-xml
- Documentação: https://clickin.github.io/stax-xml
- NPM: https://www.npmjs.com/package/stax-xml
- Licença: MIT
※ Observação sobre licença: esta biblioteca foi inspirada no conceito de StAX proposto em JSR 173: Streaming API for XML, mas não consegui determinar com clareza os termos de licença do próprio JSR. Se alguém souber sobre as cláusulas de licença do nome StAX, agradeço qualquer orientação.
1 comentários
Gostei porque dá para sentir o cuidado e a dedicação no texto.
Li tudo direitinho!