Elke SOAP-service die je hebt aangeraakt, elke RSS-feed die je hebt geconsumeerd, elke SVG die je hebt gemanipuleerd — het is allemaal XML. JavaScript heeft twee ingebouwde manieren om het in de browser te parsen, en een solide npm-bibliotheek voor Node.js. Het lastige deel is niet het parsen zelf; het is navigeren door de resulterende DOM, het omgaan met namespaces en niet verrast worden door de eigenaardigheden die iedereen de eerste keer treffen. Laten we de echte patronen doornemen.

DOMParser — XML Parsen in de Browser

De ingebouwde DOMParser-API van de browser converteert een XML-string naar een DOM-document. Gebruik het MIME-type 'application/xml' (niet 'text/html') zodat de parser strikte XML-regels toepast:

js
const xmlString = `<?xml version="1.0" encoding="UTF-8"?>
<library>
  <book isbn="978-0-13-110362-7">
    <title>The C Programming Language</title>
    <authors>
      <author>Brian W. Kernighan</author>
      <author>Dennis M. Ritchie</author>
    </authors>
    <year>1988</year>
    <price currency="USD">45.99</price>
  </book>
  <book isbn="978-0-201-63361-0">
    <title>The Pragmatic Programmer</title>
    <authors>
      <author>Andrew Hunt</author>
      <author>David Thomas</author>
    </authors>
    <year>1999</year>
    <price currency="USD">52.00</price>
  </book>
</library>`;

const parser = new DOMParser();
const doc = parser.parseFromString(xmlString, 'application/xml');

// Controleer altijd eerst op parseerfouten
const parseError = doc.querySelector('parsererror');
if (parseError) {
  throw new Error('XML parse failed: ' + parseError.textContent);
}

console.log(doc.documentElement.tagName); // library
Controleer altijd op parsererror. Anders dan JSON.parse() dat een uitzondering gooit, retourneert DOMParser een document met een <parsererror>-element wanneer parsen mislukt — er wordt geen uitzondering gegenereerd. Als je de foutcontrole overslaat, werk je stilletjes op een misvormd document en krijg je verwarrende resultaten verderop.

Navigeren door de DOM — getElementsByTagName vs querySelector

Zodra je een geparseerd document hebt, zijn er twee hoofd-API's om elementen te vinden. Beide werken, maar ze hebben verschillende sterktes:

js
// getElementsByTagName — retourneert een live HTMLCollection
const books = doc.getElementsByTagName('book');
console.log(books.length); // 2

// querySelector / querySelectorAll — CSS-selectorsyntaxis, retourneert NodeList
const firstTitle = doc.querySelector('title').textContent;
console.log(firstTitle); // The C Programming Language

// Haal alle titels op
const titles = [...doc.querySelectorAll('title')].map(el => el.textContent);
console.log(titles);
// ['The C Programming Language', 'The Pragmatic Programmer']

// Attributen lezen
const firstBook = doc.querySelector('book');
const isbn = firstBook.getAttribute('isbn');
console.log(isbn); // 978-0-13-110362-7

// Het currency-attribuut van price lezen
const priceEl = firstBook.querySelector('price');
console.log(priceEl.textContent);           // 45.99
console.log(priceEl.getAttribute('currency')); // USD

Ik geef de voorkeur aan querySelector voor gerichte zoekopdrachten — de CSS-selectorsyntaxis is vertrouwd en beknopt. Gebruik getElementsByTagName wanneer je alle elementen met een bepaalde tag nodig hebt en een live-collectie wilt (hoewel in de praktijk een gespreide NodeList meestal overzichtelijker is).

Gestructureerde Data Extraheren — Een Praktisch Patroon

Zo wijs je een XML-document toe aan een schone JavaScript-array van objecten — het patroon dat je gebruikt bij het consumeren van een echte XML-API-response:

js
function parseLibraryXml(xmlString) {
  const parser = new DOMParser();
  const doc = parser.parseFromString(xmlString, 'application/xml');

  if (doc.querySelector('parsererror')) {
    throw new Error('Invalid XML');
  }

  return [...doc.querySelectorAll('book')].map(book => ({
    isbn: book.getAttribute('isbn'),
    title: book.querySelector('title').textContent.trim(),
    authors: [...book.querySelectorAll('author')].map(a => a.textContent.trim()),
    year: parseInt(book.querySelector('year').textContent, 10),
    price: {
      amount: parseFloat(book.querySelector('price').textContent),
      currency: book.querySelector('price').getAttribute('currency')
    }
  }));
}

const books = parseLibraryXml(xmlString);
console.log(books[0].title);          // The C Programming Language
console.log(books[0].authors);        // ['Brian W. Kernighan', 'Dennis M. Ritchie']
console.log(books[0].price.amount);   // 45.99

XML met Namespaces Verwerken

Namespaces zijn waar de meeste ontwikkelaars vastlopen. SOAP-responses, Atom-feeds en SVG gebruiken allemaal XML-namespaces — en een naïeve querySelector('body') retourneert null op een SOAP-document omdat het element eigenlijk soap:Body is. Zo verwerk je het correct:

js
const soapResponse = `<?xml version="1.0"?>
<soap:Envelope
  xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:m="http://www.example.com/orders">
  <soap:Header/>
  <soap:Body>
    <m:GetOrderResponse>
      <m:OrderId>ORD-5521</m:OrderId>
      <m:Status>Shipped</m:Status>
      <m:Total currency="EUR">289.50</m:Total>
    </m:GetOrderResponse>
  </soap:Body>
</soap:Envelope>`;

const parser = new DOMParser();
const doc = parser.parseFromString(soapResponse, 'application/xml');

// Optie 1: getElementsByTagNameNS — expliciete namespace-URI
const SOAP_NS = 'http://schemas.xmlsoap.org/soap/envelope/';
const ORDER_NS = 'http://www.example.com/orders';

const body = doc.getElementsByTagNameNS(SOAP_NS, 'Body')[0];
const orderId = doc.getElementsByTagNameNS(ORDER_NS, 'OrderId')[0].textContent;
const status = doc.getElementsByTagNameNS(ORDER_NS, 'Status')[0].textContent;

console.log(orderId); // ORD-5521
console.log(status);  // Shipped

// Optie 2: XPath met namespace-resolver (flexibeler)
function nsResolver(prefix) {
  const namespaces = {
    soap: 'http://schemas.xmlsoap.org/soap/envelope/',
    m: 'http://www.example.com/orders'
  };
  return namespaces[prefix] || null;
}

const xpathResult = doc.evaluate(
  '//m:OrderId',
  doc,
  nsResolver,
  XPathResult.STRING_TYPE,
  null
);
console.log(xpathResult.stringValue); // ORD-5521

XPath-query's met evaluate()

XPath is een querytaal voor XML-documenten. De browser stelt deze beschikbaar via document.evaluate(). Het is krachtiger dan CSS-selectors voor XML — je kunt query's uitvoeren op attribuutwaarde, positie, tekstinhoud en afkomst. Zie de MDN XPath-documentatie voor de volledige expressiesyntaxis:

js
// Gebruik ons XML-bibliotheekdocument van eerder
function xpath(doc, expression, contextNode = doc) {
  const result = doc.evaluate(
    expression,
    contextNode,
    null,  // namespace-resolver — null voor XML zonder namespaces
    XPathResult.ANY_TYPE,
    null
  );
  return result;
}

// Haal alle boeketitels op
const titlesResult = xpath(doc, '//book/title');
const titles = [];
let node;
while ((node = titlesResult.iterateNext())) {
  titles.push(node.textContent);
}
console.log(titles);
// ['The C Programming Language', 'The Pragmatic Programmer']

// Haal het boek met een specifiek ISBN op
const bookResult = doc.evaluate(
  '//book[@isbn="978-0-13-110362-7"]/title',
  doc, null,
  XPathResult.STRING_TYPE,
  null
);
console.log(bookResult.stringValue); // The C Programming Language

// Haal boeken op met een prijs boven $50
const expensiveResult = xpath(doc, '//book[price > 50]/title');
let expensiveNode;
while ((expensiveNode = expensiveResult.iterateNext())) {
  console.log(expensiveNode.textContent); // The Pragmatic Programmer
}

Node.js — fast-xml-parser (de Beste Keuze)

Node.js heeft geen DOMParser. Je hebt twee opties: de op ingebouwde node:stream gebaseerde SAX-aanpak gebruiken (omslachtig), of fast-xml-parser gebruiken (de juiste keuze voor de meeste gebruiksscenario's). Het is snel, heeft nul afhankelijkheden en retourneert gewone JavaScript-objecten:

bash
npm install fast-xml-parser
js
import { XMLParser } from 'fast-xml-parser';

const xmlString = `<?xml version="1.0"?>
<library>
  <book isbn="978-0-13-110362-7">
    <title>The C Programming Language</title>
    <year>1988</year>
    <price currency="USD">45.99</price>
  </book>
  <book isbn="978-0-201-63361-0">
    <title>The Pragmatic Programmer</title>
    <year>1999</year>
    <price currency="USD">52.00</price>
  </book>
</library>`;

const parser = new XMLParser({
  ignoreAttributes: false,     // neem XML-attributen mee
  attributeNamePrefix: '@_',   // prefix attributen om ze te onderscheiden van elementen
  isArray: (tagName) => tagName === 'book'  // behandel <book> altijd als array
});

const result = parser.parse(xmlString);
const books = result.library.book;

books.forEach(book => {
  console.log(book.title);       // The C Programming Language
  console.log(book['@_isbn']);   // 978-0-13-110362-7
  console.log(book.price['#text']);       // 45.99
  console.log(book.price['@_currency']); // USD
});
De isArray-optie is cruciaal. Als je XML een lijstelement heeft dat soms één item bevat en soms meerdere, geeft fast-xml-parser je een object voor één item en een array voor meerdere. De isArray-optie dwingt consistent arraygedrag af voor benoemde tags — gebruik hem altijd voor elementen waarvan je weet dat ze kunnen herhalen.

Foutafhandeling in Node.js

js
import { XMLParser, XMLValidator } from 'fast-xml-parser';

function parseXmlSafely(xmlString) {
  // Valideer eerst — retourneert true of een foutobject
  const validation = XMLValidator.validate(xmlString);
  if (validation !== true) {
    throw new Error(`Invalid XML: ${validation.err.msg} at line ${validation.err.line}`);
  }

  const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' });
  return parser.parse(xmlString);
}

try {
  const data = parseXmlSafely(xmlString);
  console.log(data);
} catch (err) {
  console.error('XML parsing failed:', err.message);
}

Gerelateerde Tools

Bij het werken met XML in JavaScript-projecten: XML Formatter om geminimaliseerde responses op te maken, XML Validator om goed gevormd zijn te controleren vóór het parsen, XML XPath Tester om te experimenteren met XPath-query's, en XML naar JSON als je wilt converteren naar een eenvoudigere structuur.

Samenvatting

In de browser is DOMParser met 'application/xml' je beste optie — vergeet alleen niet te controleren op parsererror. Voor XML met namespaces gebruik je getElementsByTagNameNS of XPath met een namespace-resolver. In Node.js geeft fast-xml-parser je schone JavaScript-objecten zonder de DOM-overhead. De patronen hier dekken 95% van de reële XML-parseerscenario's — SOAP-responses, RSS-feeds, configuratiebestanden en meer.