Enhver SOAP-tjeneste du har berørt, enhver RSS-feed du har konsumert, enhver SVG du har manipulert — det er alt XML. JavaScript har to innebygde måter å parse det på i nettleseren, og et solid npm-bibliotek for Node.js. Den vanskelige delen er ikke selve parsingen; det er å navigere i den resulterende DOM-en, håndtere navnerom, og å unngå fallgruvene som fanger alle første gang. La oss gå gjennom de virkelige mønstrene.

DOMParser — Parsing av XML i nettleseren

Nettleserens innebygde DOMParser-API konverterer en XML-streng til et DOM-dokument. Bruk MIME-typen 'application/xml' (ikke 'text/html') slik at parseren anvender strenge XML-regler:

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');

// Sjekk alltid for parse-feil først
const parseError = doc.querySelector('parsererror');
if (parseError) {
  throw new Error('XML parse failed: ' + parseError.textContent);
}

console.log(doc.documentElement.tagName); // library
Sjekk alltid for parsererror. I motsetning til JSON.parse() som kaster et unntak, returnerer DOMParser et dokument som inneholder et <parsererror>-element når parsing mislykkes — det kaster ikke et unntak. Hvis du hopper over feilsjekken, vil du stille operere på et feilformet dokument og få forvirrende resultater nedstrøms.

Navigering i DOM — getElementsByTagName vs querySelector

Når du har et parset dokument, har du to hoved-API-er for å finne elementer. Begge fungerer, men de har forskjellige styrker:

js
// getElementsByTagName — returnerer en levende HTMLCollection
const books = doc.getElementsByTagName('book');
console.log(books.length); // 2

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

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

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

// Lese currency-attributtet fra price
const priceEl = firstBook.querySelector('price');
console.log(priceEl.textContent);           // 45.99
console.log(priceEl.getAttribute('currency')); // USD

Jeg foretrekker querySelector for målrettede oppslag — CSS-selektorsyntaksen er kjent og kortfattet. Bruk getElementsByTagName når du trenger alle elementer med en gitt tagg og ønsker en levende samling (selv om en spredt NodeList i praksis vanligvis er renere).

Utvinning av strukturerte data — et praktisk mønster

Slik mapper du et XML-dokument til en ren JavaScript-array av objekter — mønsteret du bruker når du konsumerer et ekte XML API-svar:

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

Håndtering av XML med navnerom

Navnerom er der de fleste utviklere treffer en vegg. SOAP-svar, Atom-feeder og SVG bruker alle XML-navnerom — og et naivt querySelector('body') returnerer null på et SOAP-dokument fordi elementet faktisk er soap:Body. Slik håndterer du det korrekt:

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');

// Alternativ 1: getElementsByTagNameNS — eksplisitt navnerom-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

// Alternativ 2: XPath med navnerom-resolver (mer fleksibelt)
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-spørringer med evaluate()

XPath er et spørrespråk for XML-dokumenter. Nettleseren eksponerer det via document.evaluate(). Det er kraftigere enn CSS-selektorer for XML — du kan spørre etter attributtverdi, posisjon, tekstinnhold og slektskap. Se MDN XPath-dokumentasjonen for fullstendig uttrykkssyntaks:

js
// Bruker vårt biblioteks-XML-dokument fra tidligere
function xpath(doc, expression, contextNode = doc) {
  const result = doc.evaluate(
    expression,
    contextNode,
    null,  // navnerom-resolver — null for XML uten navnerom
    XPathResult.ANY_TYPE,
    null
  );
  return result;
}

// Hent alle boktitler
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']

// Hent boken med et bestemt ISBN
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

// Hent bøker priset over $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 (det beste valget)

Node.js har ingen DOMParser. Du har to alternativer: bruk den innebygde SAX-baserte strøm-tilnærmingen med node:stream (tungvint), eller bruk fast-xml-parser (det riktige valget for de fleste brukstilfeller). Den er rask, uten avhengigheter, og returnerer vanlige JavaScript-objekter:

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,     // inkluder XML-attributter
  attributeNamePrefix: '@_',   // prefiks attributter for å skille fra elementer
  isArray: (tagName) => tagName === 'book'  // behandle alltid <book> som en 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
});
Alternativet isArray er avgjørende. Hvis XML-en din har et listeelement som noen ganger inneholder ett element og noen ganger mange, gir fast-xml-parser deg et objekt for ett element og en array for mange. Alternativet isArray tvinger konsekvent array-atferd for navngitte tagger — bruk det alltid for elementer du vet kan gjenta seg.

Feilhåndtering i Node.js

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

function parseXmlSafely(xmlString) {
  // Valider først — returnerer true eller et feilobjekt
  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);
}

Relaterte verktøy

Når du jobber med XML i JavaScript-prosjekter: XML Formatter for å formatere minifiserte svar, XML Validator for å sjekke velformethet før parsing, XML XPath Tester for å eksperimentere med XPath-spørringer, og XML to JSON hvis du vil konvertere til en enklere struktur.

Oppsummering

I nettleseren er DOMParser med 'application/xml' ditt foretrukne valg — husk bare å sjekke for parsererror. For XML med navnerom, bruk getElementsByTagNameNS eller XPath med en navnerom-resolver. I Node.js gir fast-xml-parser deg rene JavaScript-objekter uten DOM-overhead. Mønstrene her dekker 95% av virkelige XML-parsing-scenarier — SOAP-svar, RSS-feeder, konfigurasjonsfiler og mer.