Skip to main content

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.

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>Deno 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: {
    title: string;
    entry: { "@id": string; "@draft": string; title: string }[];
  };
};

console.log(doc.feed.title); // Deno 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 Deno CLI:

deno run https://docs.deno.com/examples/scripts/parse_xml.ts

Additional resources

Did you find what you needed?

Privacy policy