Alasan kami menulis ini: kami adalah Geonode dan kami menjual proxy kepada orang-orang yang mengumpulkan data, dan sebagian besar data tersebut datang dalam bentuk XML — peta situs, umpan RSS dan Atom, respons SOAP, serta katalog produk. Perlu dicatat bahwa kegagalan parsing hampir tidak pernah disebabkan oleh masalah jaringan. Jika parser XML Anda melaporkan kesalahan, tampilkan 200 karakter pertama dari data yang Anda terima sebelum mengubah kode apa pun. Sembilan dari sepuluh kali, itu adalah halaman kesalahan HTML, dan parser melaporkan dengan akurat bahwa Anda tidak menerima XML.
Di Browser: DOMParser
Sudah terintegrasi, tanpa ketergantungan.
const parser = new DOMParser();
const doc = parser.parseFromString(xmlString, "application/xml");
const titles = doc.querySelectorAll("item > title");
titles.forEach(t => console.log(t.textContent));
Dokumen MDN mencantumkan tipe MIME yang didukung sebagai text/html
, text/xml
, application/xml
, application/xhtml+xml
, dan image/svg+xml
. Fungsi ini mengembalikan sebuah Document
"dengan properti contentType
yang sesuai dengan mimeType
yang diberikan", yang bisa berupa HTMLDocument
atau XMLDocument
tergantung pada apa yang Anda minta.
Gunakan application/xml
untuk XML. Menggunakan text/html
akan memanggil parser HTML, yang bersifat fleksibel sehingga dapat mengubah dokumen Anda tanpa pemberitahuan — parser ini tidak akan menghormati sensitivitas huruf besar-kecil dalam XML, dan akan dengan senang hati menerima hal-hal yang dilarang oleh XML.
Setelah diparsing, Anda akan mendapatkan DOM. Semua yang Anda ketahui tentang navigasi DOM berlaku: querySelector
, querySelectorAll
, getElementsByTagName
, children
, textContent
.
Perangkap Kesalahan: Tidak Memicu Pengecualian
Perilaku yang selalu mengejutkan semua orang saat pertama kali menemui.
Berikan XML yang tidak valid ke DOMParser, dan hal itu tidak akan memicu pengecualian. MDN secara eksplisit menyatakan: "XMLDocument yang dikembalikan akan berisi node <parsererror> yang menjelaskan kesalahan parsing", dan kesalahan tersebut "juga dapat dilaporkan ke konsol JavaScript browser".
Jadi, Anda harus memeriksa:
const doc = parser.parseFromString(xmlString, "application/xml");
const errorNode = doc.querySelector("parsererror");
if (errorNode) {
throw new Error(`XML parse failed: ${errorNode.textContent}`);
}
Tanpa pemeriksaan tersebut, dokumen yang tidak valid akan menghasilkan objek Document yang berisi pesan kesalahan di tempat seharusnya data Anda berada. Panggilan querySelectorAll berikutnya tidak akan mengembalikan apa pun, dan gejalanya tampak seperti masalah selektor daripada kegagalan parsing.
Bungkus dengan satu kali:
function parseXml(text) {
const doc = new DOMParser().parseFromString(text, "application/xml");
const err = doc.querySelector("parsererror");
if (err) {
throw new Error(
`XML parse failed: ${err.textContent.trim()}. ` +
`First 200 chars: ${text.slice(0, 200)}`
);
}
return doc;
}
Bagian text.slice(0, 200) adalah yang menghemat waktu. Kesalahan parsing memberi tahu Anda bahwa input tidak valid; 200 karakter pertama memberi tahu Anda bahwa itu adalah halaman kesalahan HTML.
Di Node: Memilih Pustaka
Node tidak memiliki parser XML bawaan, sehingga hal ini bergantung pada pilihan dependensi. Angka unduhan mingguan memberikan gambaran kasar mengenai tingkat adopsi — data ini diambil dari registri npm, yang diperiksa pada September 2026.
| Pustaka | Unduhan mingguan | Gaya |
|---|---|---|
sax |
| ~88,9 juta | Streaming, berbasis peristiwa |
| fast-xml-parser
| ~85,0 juta | XML ke objek JavaScript biasa |
| @xmldom/xmldom
| ~48,7 juta | Implementasi DOM untuk Node |
| xml2js
| ~44,5 juta | Konversi XML ke objek, API callback dan promise |
| xpath
| ~12,2 juta | Kueri XPath, berpasangan dengan xmldom |
**fast-xml-parser
** adalah pilihan default yang praktis untuk sebagian besar pekerjaan. Perpustakaan ini mengonversi XML menjadi objek JavaScript biasa, yang berarti Anda menavigasi menggunakan notasi titik (dot notation) alih-alih metode DOM:
import { XMLParser } from "fast-xml-parser";
const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "@" });
const obj = parser.parse(xmlString);
console.log(obj.rss.channel.item[0].title);
Hal yang perlu dipahami: konversi objek memiliki kerugian dalam satu hal tertentu. Elemen yang muncul sekali akan menjadi objek; elemen yang sama yang muncul dua kali akan menjadi array. Jadi, channel.item
adalah array untuk feed dengan tiga item dan objek untuk feed dengan satu item — dan kode yang mengasumsikan adanya array akan gagal pada kasus dengan satu item. Sebagian besar pustaka menawarkan opsi untuk selalu menghasilkan array untuk elemen bernama, dan mengaktifkannya untuk apa pun yang akan Anda iterasi layak dilakukan sebelum hal itu menimbulkan masalah.
**@xmldom/xmldom
** memberi Anda DOM asli di Node, yang penting jika Anda ingin kode yang sama berfungsi di kedua lingkungan atau jika Anda memerlukan XPath. Padukan dengan paket xpath
:
import { DOMParser } from "@xmldom/xmldom";
import xpath from "xpath";
const doc = new DOMParser().parseFromString(xmlString, "text/xml");
const titles = xpath.select("//item/title/text()", doc);
**sax
** adalah parser streaming yang memancarkan peristiwa saat membaca. Ini adalah solusi untuk dokumen yang terlalu besar untuk disimpan di memori — indeks peta situs berukuran multi-gigabyte, ekspor katalog massal — di mana pendekatan DOM tidak akan cocok.
**xml2js
** telah lama ada dan digunakan secara luas. Antarmuka pemrogramannya (API) memang agak ketinggalan zaman tetapi masih dapat diandalkan, dan terdapat banyak kode yang sudah ada yang menggunakannya.
Ruang Nama: Bagian yang Mengacaukan Dokumen Asli
Alasan paling umum mengapa sebuah selektor yang semula berfungsi tiba-tiba tidak menemukan apa pun.
Banyak format XML nyata mendeklarasikan namespace:
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://example.com/</loc></url>
</urlset>
xmlns
menempatkan setiap elemen dalam namespace default. Dalam parser yang mendukung namespace, <loc>
bukanlah sekadar loc
— melainkan loc
di dalam namespace sitemaps, dan getElementsByTagName("loc")
biasa mungkin tidak menemukan apa pun.
Tiga cara untuk mengatasinya, dalam urutan yang semakin tepat.
Gunakan metode yang mendukung namespace:
const NS = "http://www.sitemaps.org/schemas/sitemap/0.9";
const locs = doc.getElementsByTagNameNS(NS, "loc");
Gunakan namespace wildcard jika Anda tidak mempermasalahkan namespace mana yang digunakan:
const locs = doc.getElementsByTagNameNS("*", "loc");
Konfigurasikan pustaka Anda untuk mengabaikan namespace. Sebagian besar pustaka pemetaan objek memiliki opsi untuk menghapus awalan namespace, yang menghasilkan kunci loc
biasa. Cara ini praktis, dan akan secara diam-diam menggabungkan dua elemen yang benar-benar berbeda namun kebetulan memiliki nama lokal yang sama — hal ini dapat diterima untuk peta situs, namun berbahaya untuk dokumen yang mencampurkan berbagai kosakata.
Perhatikan bahwa querySelector
berperilaku berbeda dari getElementsByTagNameNS
di sini: selektor CSS memiliki sintaks namespace tersendiri yang canggung dan jarang digunakan, sehingga untuk XML dengan namespace, metode NS
atau XPath lebih dapat diandalkan.
XPath dalam JavaScript
Tersedia di peramban melalui document.evaluate, dan di Node melalui paket xpath dengan perintah xmldom.
const result = doc.evaluate(
"//item/title/text()",
doc,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
for (let i = 0; i < result.snapshotLength; i++) {
console.log(result.snapshotItem(i).nodeValue);
}
API ini cukup rumit sehingga kebanyakan orang hanya menggunakannya sekali lalu melupakannya. Yang membuatnya layak dicoba adalah bahwa XPath dapat mengekspresikan hal-hal yang tidak bisa dilakukan oleh selektor CSS — pencocokan berdasarkan konten teks, menelusuri ke elemen induk, dan logika posisi relatif terhadap elemen saudara.
Ada dua batasan. Browser mengimplementasikan XPath 1.0, jadi tidak ada matches(), tidak ada lower-case(), dan tidak ada ends-with(). Selain itu, ruang nama memerlukan fungsi resolver — argumen ketiga — yang memetakan prefiks ke URI ruang nama. Menggunakan null hanya berfungsi untuk dokumen tanpa ruang nama, yang mengecualikan sebagian besar feed yang sebenarnya.
Untuk dokumen dengan namespace di peramban:
const resolver = prefix => ({ sm: "http://www.sitemaps.org/schemas/sitemap/0.9" }[prefix] || null);
const result = doc.evaluate("//sm:loc/text()", doc, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
Perhatikan bahwa Anda menentukan prefiksnya sendiri — sm di sini — terlepas dari apa yang digunakan dokumen tersebut, karena XPath 1.0 tidak memiliki konsep namespace default.
Mengonversi XML ke JSON, dan Apa yang Hilang
Hal yang paling sering diinginkan orang, dan perlu dipahami bahwa proses konversi ini tidak bebas kehilangan data.
XML dan JSON memiliki model data yang berbeda. XML memiliki atribut, elemen, node teks, komentar, instruksi pemrosesan, ruang nama, dan urutan. JSON memiliki objek, larik, string, bilangan, nilai boolean, dan null. Ada empat hal yang tidak dapat dipertahankan dengan sempurna selama proses konversi.
Atribut versus elemen anak. <item id="1"><name>x</name></item> memiliki atribut dan elemen anak, sedangkan JSON tidak membedakan keduanya. Pustaka menangani hal ini dengan menambahkan awalan pada kunci atribut — umumnya @ atau $ — yang harus Anda konfigurasikan dan ingat:
const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "@" });
// { item: { "@id": "1", name: "x" } }
Elemen yang berulang menjadi array secara tidak konsisten. Telah dibahas di atas dan layak diulang karena ini adalah bug paling umum di bidang ini: satu kemunculan menghasilkan objek, dua kemunculan menghasilkan array. Atur opsi "always array" pada pustaka untuk setiap elemen yang ingin Anda iterasi.
Konten campuran tidak memiliki representasi yang rapi. <p>Hello <b>world</b>!</p> mencampurkan teks dan elemen. Saat dikonversi menjadi objek, fragmen teks dan posisinya relatif terhadap elemen anak sulit direpresentasikan, dan sebagian besar pustaka akan menggabungkan teks tersebut atau menghilangkan sebagian darinya. Jika XML Anda berisi markup prosa, konversi ke objek bukanlah pendekatan yang tepat — pertahankan DOM.
Urutan tidak dijamin. Kunci objek JSON tidak memiliki urutan yang didefinisikan dalam model data, sehingga dokumen di mana urutan elemen memiliki makna akan kehilangan informasi tersebut. Array mempertahankan urutan; elemen saudara dengan nama berbeda tidak.
Dan semuanya menjadi string kecuali Anda meminta sebaliknya. XML tidak memiliki tipe data, sehingga <price>42.50</price> adalah teks. Sebagian besar pustaka menawarkan konversi numerik, yang praktis dan dengan senang hati akan mengubah kode produk dengan nol di depan menjadi angka serta string versi menjadi bilangan desimal. Untuk apa pun yang merupakan pengenal (identifier) alih-alih kuantitas, matikan konversi tersebut.
Panduan praktis: konversi objek cocok untuk XML berbentuk data — feed, katalog, konfigurasi, respons API — di mana elemen-elemennya berupa catatan dan bidang. Gunakan DOM untuk XML berbentuk dokumen, di mana markup tertanam dalam teks dan strukturnya mengandung makna.
Keamanan: XXE dan Injeksi
Dua risiko yang berbeda, keduanya nyata.
Pemrosesan Entitas Eksternal XML. XML dapat mendeklarasikan entitas yang merujuk pada sumber daya eksternal, termasuk berkas lokal dan URL jaringan. Parser yang memproses entitas tersebut dapat dimanipulasi untuk membaca file dari server atau melakukan permintaan atas nama penyerang. Ini adalah jenis kerentanan klasik yang masih sering ditemui.
Upaya mitigasi adalah dengan menonaktifkan pemrosesan entitas eksternal dan DTD pada parser apa pun yang Anda gunakan. DOMParser-browser tidak memproses entitas eksternal, sehingga kasus browser aman secara default. Pustaka Node bervariasi, dan hal ini perlu diperiksa daripada diasumsikan — jika Anda mem-parsing XML dari sumber yang tidak tepercaya di Node, pastikan penanganan entitas parser Anda sebelum dirilis.
Injeksi saat menyisipkan kembali ke dalam DOM. MDN memperingatkan bahwa parseFromString "merupakan 'injection sink' dan vektor potensial untuk serangan XSS jika input berasal dari penyerang". Nuansanya penting: dengan text/html, "elemen<script> ditandai sebagai tidak dapat dieksekusi dan penangan peristiwa tidak dipanggil" — tetapi skrip "akan berjalan jika dokumen yang diparsing kemudian disisipkan ke dalam DOM yang terlihat".
Jadi, mem-parsing aman; menyisipkan hasilnya tidak. Rekomendasi MDN adalah untuk meneruskan objek TrustedHTML alih-alih string, menerapkan tipe tepercaya melalui direktif CSP require-trusted-types-for, dan melakukan sanitasi dengan pustaka seperti DOMPurify melalui TrustedTypePolicy.
Aturan sederhananya: jangan pernah menyisipkan markup yang telah diparsing dan tidak tepercaya ke dalam DOM aktif tanpa melakukan sanitasi terlebih dahulu, dan utamakan textContent daripada innerHTML jika Anda hanya membutuhkan teksnya.
Pola Praktis
Menganalisis umpan RSS atau Atom:
const doc = parseXml(await res.text());
const items = [...doc.getElementsByTagNameNS("*", "item")].map(item => ({
title: item.getElementsByTagNameNS("*", "title")[0]?.textContent?.trim(),
link: item.getElementsByTagNameNS("*", "link")[0]?.textContent?.trim(),
date: item.getElementsByTagNameNS("*", "pubDate")[0]?.textContent?.trim(),
}));
Ruang nama wildcard dapat menangani baik RSS maupun Atom tanpa perlu percabangan, dan rantai opsional dapat menangani umpan yang memiliki bidang yang hilang — yang merupakan sebagian besar di antaranya.
Menganalisis peta situs, termasuk berkas indeks:
const doc = parseXml(xml);
const isIndex = doc.documentElement.localName === "sitemapindex";
const locs = [...doc.getElementsByTagNameNS("*", "loc")].map(n => n.textContent.trim());
// if isIndex, these are sitemap URLs to fetch; otherwise they are page URLs
Memeriksa atribut ``localName`
pada elemendocument adalah cara yang andal untuk membedakan keduanya, karena keduanya mengandung elemen ``<loc>
` dan hanya pembungkusnya yang berbeda.
Menangani dokumen besar — gunakan parser streaming daripada membangun DOM:
import sax from "sax";
const stream = sax.createStream(true, { trim: true });
let current = null;
stream.on("opentag", node => { if (node.name === "loc") current = ""; });
stream.on("text", t => { if (current !== null) current += t; });
stream.on("closetag", name => { if (name === "loc") { emit(current); current = null; } });
Memori tetap terlepas dari ukuran dokumen, dengan biaya penulisan mesin keadaan kecil.
Pertanyaan Terkait
Bagaimana cara mengurai XML di JavaScript?
Di peramban, gunakan DOMParser bawaan: new DOMParser().parseFromString(xml, "application/xml") akan mengembalikan Document yang dapat Anda akses menggunakan metode DOM. Di Node.js, tidak ada parser bawaan, jadi Anda perlu menginstalnya — fast-xml-parser untuk konversi objek, @xmldom/xmldom untuk DOM yang sesungguhnya.
Mengapa DOMParser tidak melempar pengecualian pada XML yang tidak valid?
Itu memang dirancang demikian. Alih-alih melempar pengecualian, dokumen yang dikembalikan berisi node <parsererror> yang menjelaskan kegagalan tersebut. Anda harus memeriksanya secara eksplisit dengan doc.querySelector("parsererror"), atau dokumen yang tidak valid akan diam-diam menghasilkan hasil kueri kosong.
Apakah Node.js memiliki parser XML bawaan?
Tidak. Berbeda dengan JSON, XML memerlukan dependensi. Opsi yang umum digunakan adalah fast-xml-parser dan xml2js untuk mengonversi ke objek, @xmldom/xmldom untuk implementasi DOM, serta sax untuk memproses dokumen berukuran sangat besar secara streaming.
Mengapa selektor XML saya tidak menemukan apa pun?
Biasanya karena namespace. Dokumen yang mendeklarasikan xmlns akan menempatkan setiap elemen di namespace tersebut, dan getElementsByTagName biasa mungkin tidak cocok. Gunakan getElementsByTagNameNS dengan URI namespace, atau "*" sebagai karakter pengganti, atau konfigurasikan pustaka Anda untuk mengabaikan namespace.
Bagaimana cara menggunakan XPath dengan XML di JavaScript?
Di peramban, gunakan document.evaluate dengan tipe XPathResult — cukup rinci untuk dibungkus sekali. Di Node, gunakan paket xpath yang dipasangkan dengan @xmldom/xmldom. Perhatikan bahwa peramban hanya mengimplementasikan XPath 1.0, dan dokumen dengan namespace memerlukan fungsi resolver yang memetakan prefiks ke URI.
Apakah mengurai XML di JavaScript berisiko terhadap keamanan?
Ada dua risiko. Pemrosesan Entitas Eksternal XML dapat membuat parser membaca file lokal atau mengirimkan permintaan — browser DOMParser tidak menyelesaikan entitas eksternal, tetapi pustaka Node bervariasi dan harus diperiksa. Selain itu, menyisipkan markup yang telah diparsing dan tidak tepercaya ke dalam DOM aktif dapat menjalankan skrip, jadi lakukan sanitasi sebelum penyisipan.
Bagaimana cara mem-parsing file XML yang sangat besar?
Gunakan parser streaming seperti sax, yang memancarkan peristiwa saat membaca alih-alih membangun dokumen di memori. Cara ini menangani berkas berukuran berapa pun dengan penggunaan memori konstan, dengan biaya penulisan mesin status kecil untuk melacak posisi Anda.
Mengapa XML yang telah diparsing terkadang menghasilkan array dan terkadang objek?
Karena pustaka pemetaan objek hanya menghasilkan array ketika suatu elemen berulang. Feed dengan tiga item akan menghasilkan array; feed yang sama dengan satu item akan menghasilkan objek. Sebagian besar pustaka memiliki opsi untuk selalu menghasilkan array untuk elemen bernama — aktifkan opsi ini untuk apa pun yang Anda iterasi.
Kesimpulan
Lingkungan kerja sangat menentukan hal ini. Di peramban, Anda memiliki DOMParser dan tidak ada ketergantungan; di Node, Anda memilih sebuah pustaka, dan pilihan tersebut menentukan cara Anda menulis semua kode selanjutnya.
Dua perilaku ini menjadi penyebab utama waktu yang terbuang. DOMParser melaporkan kegagalan dengan node parsererror alih-alih pengecualian (exception), sehingga dokumen yang tidak valid menghasilkan hasil kosong yang tampak seperti masalah selektor — periksa node tersebut, dan catat 200 karakter pertama dari input, karena biasanya itu adalah halaman kesalahan HTML. Selain itu, namespace secara diam-diam mengacaukan pencarian nama tag biasa pada dokumen-dokumen yang paling ingin Anda parsing: sitemap, feed, dan respons SOAP semuanya mendeklarasikan namespace.
Selain itu, sesuaikan alat dengan ukuran data. Pemetaan objek untuk dokumen biasa, DOM asli saat Anda memerlukan XPath atau kode browser dan server yang dibagikan, serta parser streaming saat file terlalu besar untuk disimpan. Dan jika sumbernya tidak tepercaya, periksa penanganan entitas eksternal pada parser Node Anda sebelum dirilis — hal tersebut telah menjadi kelas kerentanan selama dua dekade dan masih tetap demikian hingga kini.
