Parse and generate XML

The @libs/xml package on JSR converts XML in both directions: XML text to plain JavaScript objects and back. This is enough for RSS feeds, sitemaps, SVG manipulation, and API responses from older services.

Edit on GitHub

import { parse, stringify } from "jsr:@libs/xml";

An RSS-style document with attributes, nesting, and repeated elements.

const xml = `<?xml version="1.0" encoding="UTF-8"?>
<feed>
  <title>Ende releases</title>
  <entry id="1" draft="false">
    <title>Deno 2.0</title>
  </entry>
  <entry id="2" draft="true">
    <title>Deno 2.1</title>
  </entry>
</feed>`;

parse returns nested objects. Attributes are prefixed with @, text content of a leaf element becomes its value, and repeated elements become an array.

const doc = parse(xml) as unknown as {
  feed: {
    entry: { "@draft": string; "@id": string; title: string; }[];
    title: string;
  };
};

console.log(doc.feed.title); // Ende releases
console.log(doc.feed.entry.length); // 2
console.log(doc.feed.entry[1]["@id"]); // 2

Values keep their string form unless you opt into revival; attributes like draft="true" can be compared as strings or revived to booleans via the reviver options of parse.

const drafts = doc.feed.entry.filter((e) => e["@draft"] === "true");
console.log(drafts.map((e) => e.title)); // [ "Deno 2.1" ]

stringify performs the reverse mapping: objects with @-prefixed keys become attributes, everything else becomes elements.

const generated = stringify({
  sitemap: {
    url: [
      { loc: "https://example.com/", "@priority": "1.0" },
      { loc: "https://example.com/docs", "@priority": "0.8" }
    ]
  }
});

console.log(generated);

<sitemap> <url priority="1.0"> <loc>https://example.com/</loc> </url> <url priority="0.8"> <loc>https://example.com/docs</loc> </url> </sitemap>

Run this example locally using the Ende CLI:

ende run https://docs.ende.sh/examples/scripts/parse_xml.ts

Additional resources