11 points par click 2025-09-22 | 1 commentaires | Partager sur WhatsApp

Comme mon travail repose sur des services legacy en Java qui communiquent via XML, et que je voulais créer un nouveau service web basé sur JS tout en conservant ces services legacy comme backend, je n’ai pas trouvé de parseur XML qui me convienne vraiment, alors je l’ai créé moi-même.

Il parse le XML selon une approche pull basée sur StAX et fournit une implémentation asynchrone, ce qui permet de traiter de gros fichiers XML basés sur des streams avec seulement environ 10 Mo de mémoire.
Comme la longueur maximale d’une string dans le standard ECMAScript est de 2^53 - 1, pour les XML de plus de 1 Go il fallait jusqu’ici se rabattre sur un parseur SAX, mais je pense que cette bibliothèque peut être une bonne alternative.

Comme j’utilise surtout Java au travail et que c’est la première fois que je crée une bibliothèque pour l’écosystème Node, n’hésitez pas à me faire des suggestions sur les points à améliorer ; j’essaierai d’en tenir compte autant que possible.

Historique

Au début, j’ai envisagé d’utiliser la bibliothèque Java woodstox via un binding WASM, mais à l’époque le GC n’était pas encore implémenté dans WASM, et j’ai donc estimé qu’il était encore trop tôt pour compiler Java vers WASM.
Ensuite, j’ai essayé d’utiliser quick-xml de Rust via un binding WASM, mais le coût du passage des streams à WASM était trop élevé, ce qui créait un écart de performances trop important avec les parseurs XML JavaScript existants ; j’ai donc abandonné cette piste.
J’ai finalement décidé de tout écrire en TypeScript pur, en m’aidant aussi de plusieurs IA, et j’ai appliqué diverses optimisations en ciblant le moteur V8.

🚀 Principales caractéristiques

Parsing entièrement asynchrone et basé sur les streams

  • Gros fichiers XML (de plusieurs centaines de Mo à plusieurs Go) traités efficacement en mémoire
  • Parsing en temps réel sur la base de ReadableStream sans bloquer le thread principal
  • Traitement des données en mode pull uniquement selon les besoins
// Même un fichier de 970MB peut être traité avec moins de 10MB de mémoire  
const parser = new StaxXmlParser(largeXmlStream);  
for await (const event of parser) {  
  // Traitement des événements en streaming  
}  

Parsing orienté événements dans le style StAX

Le parseur fournit un pattern de pull parser familier pour les développeurs Java, permettant un contrôle fin de la structure XML.

import { StaxXmlParser, isStartElement, isCharacters } from 'stax-xml';  
  
for await (const event of parser) {  
  if (isStartElement(event)) {  
    console.log(`Élément : ${event.name}`, event.attributes);  
  } else if (isCharacters(event)) {  
    console.log(`Texte : ${event.value}`);  
  }  
}  

🛠️ 4 composants clés

1. StaxXmlParser (parseur asynchrone)

  • Dédié aux gros fichiers : parsing basé sur des streams et économe en mémoire
  • Traitement en temps réel : parsing en direct de XML distants via l’intégration avec la fetch API
  • Type guards TypeScript : sécurité de type garantie à l’exécution

2. StaxXmlParserSync (parseur synchrone)

  • Optimisé pour les petits fichiers : parsing rapide de chaînes XML en mémoire
  • Réponses d’API web : traitement immédiat dans des workflows synchrones

3. StaxXmlWriter (writer asynchrone)

  • Génération en streaming : écrit directement le XML dans un WritableStream
  • Réponse en temps réel : génération de réponses XML volumineuses sur un serveur d’API
  • Efficace en mémoire : ne stocke pas l’intégralité du XML en mémoire

4. StaxXmlWriterSync (writer synchrone)

  • Génération immédiate : construction d’une chaîne XML en mémoire
  • Intégration serveur web : intégration parfaite avec Express, Hono, etc.

📊 Comparaison de performances (benchmark)

Environnement 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
  • Outil : Mitata

Parsing d’un gros fichier de 97MB :

  • stax-xml : 1.05s, mémoire 8.89MB
  • fast-xml-parser : 4.41s, mémoire 886.33MB
  • txml : 1.02s, mémoire 897.50MB

🌐 Compatibilité générale

Fonctionne dans tous les runtimes JavaScript en n’utilisant que les API web standard :

  • Node.js (v18+)
  • Bun, Deno
  • Navigateurs web
  • Edge Runtime (Vercel, Cloudflare Workers)

📦 Installation et démarrage

npm install stax-xml  
import { StaxXmlParser, XmlEventType } from 'stax-xml';  
  
// Création d’un stream à partir d’une chaîne XML  
const stream = new ReadableStream({  
  start(controller) {  
    controller.enqueue(new TextEncoder().encode(xmlContent));  
    controller.close();  
  }  
});  
  
// Parsing asynchrone  
const parser = new StaxXmlParser(stream);  
for await (const event of parser) {  
  if (event.type === XmlEventType.START_ELEMENT) {  
    console.log(`Élément : ${event.name}`, event.attributes);  
  }  
}  

📄 Informations sur le projet


※ Remarque concernant la licence : cette bibliothèque s’inspire du concept StAX proposé dans JSR 173: Streaming API for XML, mais je n’ai pas pu déterminer clairement les conditions de licence propres au JSR lui-même. Si quelqu’un connaît les clauses de licence liées à l’appellation StAX, ses conseils seraient les bienvenus.

1 commentaires

 
honglu 2025-09-22

On sent beaucoup de soin et d’attention dans la documentation, c’est très appréciable.

J’ai beaucoup aimé la lecture !