11 poin oleh click 2025-09-22 | 1 komentar | Bagikan ke WhatsApp

Di tempat kerja saya menggunakan layanan legacy berbasis Java+XML untuk komunikasi, dan saat mencoba membuat layanan web baru berbasis JS dengan tetap memakai layanan legacy itu sebagai backend, saya tidak menemukan parser XML yang benar-benar cocok, jadi saya membuatnya sendiri.

XML diparse dengan metode pull berbasis StAX, dan tersedia implementasi asinkron sehingga file XML berukuran besar berbasis stream pun bisa diparse hanya dengan memori sekitar 10MB.
Pada standar ecmascript, panjang maksimum string adalah 2^53 - 1, jadi untuk XML berukuran lebih dari 1GB selama ini praktis hanya bisa memakai parser SAX; saya rasa library ini bisa menjadi alternatif yang bagus.

Karena di pekerjaan saya terutama memakai Java dan ini adalah pertama kalinya saya membuat library untuk ekosistem Node, jika ada kekurangan silakan beri saran dan saya akan berusaha sebaik mungkin untuk menindaklanjutinya.

Riwayat

Awalnya saya sempat mempertimbangkan untuk membungkus library woodstox milik Java ke WASM,
namun saat itu WASM belum memiliki implementasi GC, sehingga saya merasa mengompilasi Java ke WASM masih terlalu dini dan membatalkan rencana tersebut.
Kedua, saya mencoba membungkus quick-xml dari Rust ke WASM, tetapi biaya untuk meneruskan stream ke WASM terlalu besar sehingga selisih performanya terhadap parser XML JavaScript yang sudah ada menjadi terlalu jauh, jadi saya hentikan juga.
Pada akhirnya saya memutuskan menulisnya dalam TypeScript murni, dan dengan bantuan beberapa AI saya juga menerapkan berbagai optimasi dengan target mesin V8.

🚀 Fitur utama

Parsing berbasis stream asinkron sepenuhnya

  • File XML besar (ratusan MB hingga GB) diproses secara efisien dalam penggunaan memori
  • Berbasis ReadableStream untuk parsing real-time tanpa memblokir thread utama
  • Metode pull untuk memproses data hanya sebanyak yang diperlukan
// Bahkan file 970MB dapat diproses dengan memori di bawah 10MB  
const parser = new StaxXmlParser(largeXmlStream);  
for await (const event of parser) {  
  // Menangani event secara streaming  
}  

Parsing berbasis event bergaya StAX

Menyediakan pola pull parser yang familier bagi developer Java sehingga struktur XML bisa dikontrol secara rinci.

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

🛠️ 4 komponen inti

1. StaxXmlParser (parser asinkron)

  • Khusus file besar: parsing efisien memori berbasis stream
  • Pemrosesan real-time: parsing XML jarak jauh secara real-time terhubung dengan fetch API
  • Type guard TypeScript: menjamin keamanan tipe saat runtime

2. StaxXmlParserSync (parser sinkron)

  • Dioptimalkan untuk file kecil: parsing cepat string XML in-memory
  • Respons Web API: pemrosesan langsung dalam workflow sinkron

3. StaxXmlWriter (writer asinkron)

  • Pembuatan streaming: menulis XML langsung ke WritableStream
  • Respons real-time: menghasilkan respons XML besar di server API
  • Efisien memori: tidak menyimpan seluruh XML di memori

4. StaxXmlWriterSync (writer sinkron)

  • Pembuatan instan: membangun string XML di dalam memori
  • Integrasi server web: terhubung sempurna dengan Express, Hono, dan lainnya

📊 Perbandingan performa (benchmark)

Lingkungan 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 file besar 97MB:

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

🌐 Kompatibilitas umum

Hanya menggunakan API standar web sehingga berjalan di semua runtime JavaScript:

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

📦 Instalasi dan mulai

npm install stax-xml  
import { StaxXmlParser, XmlEventType } from 'stax-xml';  
  
// Membuat stream dari string XML  
const stream = new ReadableStream({  
  start(controller) {  
    controller.enqueue(new TextEncoder().encode(xmlContent));  
    controller.close();  
  }  
});  
  
// Parsing asinkron  
const parser = new StaxXmlParser(stream);  
for await (const event of parser) {  
  if (event.type === XmlEventType.START_ELEMENT) {  
    console.log(`Elemen: ${event.name}`, event.attributes);  
  }  
}  

📄 Informasi proyek


※ Catatan terkait lisensi: Library ini terinspirasi oleh konsep StAX yang diusulkan dalam JSR 173: Streaming API for XML, namun saya belum bisa memahami dengan jelas syarat lisensi dari JSR itu sendiri. Jika ada yang mengetahui ketentuan lisensi untuk nama StAX, saya akan sangat berterima kasih atas sarannya.

1 komentar

 
honglu 2025-09-22

Saya suka karena terasa ketulusan dan kesungguhan dalam penyusunan dokumennya.

Saya membacanya dengan baik!