npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@biblioteksentralen/xml-utils

v0.0.3

Published

XML parsing utils

Readme

@biblioteksentralen/xml-utils

XML parsing and serialization utils based on saxes.

Limitations

XML has tons of different use cases. The XmlElement class focuses on handling XML as simple data containers in a safe and fast way with more or less the same expressivity as JSON + attributes. It's not intended for HTML, annotated documents or other use cases, but works well with ONIX, MARC and similar data containers.

Some limitations:

  • An element node can only contain one of the following: text, CDATA, child elements.
  • Whitespace outside of text and cdata nodes is not preserved. This also means that you don't necessarily get the exactly same document back if you stringify a document that has just been parsed. But the serialization is stable, so you get the same serialization each time.
  • Namespaces are stripped.
  • Comments are stripped.
  • No support for processing instructions or entity declarations.
  • No concept of a document node, only element nodes.

Parsing XML

The parseXml function parses an XML element (provided as a string or buffer) and returns an XmlElement instance, which includes methods for extracting data from the XML. Example:

import fs from "node:fs";
import { parseXml } from "@biblioteksentralen/xml-utils";

const source = Buffer.from(
  "<products><product>Hello world</product></products>",
  "utf-8",
);
const rootNode = await parseXml(source);
console.log(rootNode.first("product")?.text());

Iterate over top level nodes

For large XML files, it is more efficient to parse the file iteratively and discard data after it has been processed, rather than reading the entire file into memory. This approach allows files of any size to be parsed with nearly constant memory usage.

The library provides a generator method that yields top level nodes from an XML file so these can be iterated over without reading the whole file into memory. Example:

import fs from "node:fs";
import {
  streamTopLevelXmlNodes,
  parseXml,
} from "@biblioteksentralen/xml-utils";

const readable = fs.createReadStream("onix.xml", { encoding: "utf-8" });
for await (const node of streamTopLevelXmlNodes(readable)) {
  console.log(node.nodeName, node.xmlText);
  if (node.nodeName === "header") {
    const headerNode = await parseXml(node.xmlText);
    // ...
  }
}

Note: If the stream contain characters considered invalid by the XML 1.0 specification such as control characters, these can be ignored by adding the ignoreInvalidCharacters: true to streamTopLevelXmlNodes. Invalid characters will then be returned in the response, but double encoded to not cause harm. An error will also be logged.

Serializing XML

The XmlElement can be serialized back to XML:

import { serializeXml } from "./serializeXml.js";

const serialized = serializeXml(rootNode);
console.log(serialized);

To create pretty-printed (indented) XML:

const serialized = serializeXml(rootNode, true);
console.log(serialized);

Building XML

Example:

import {
  createXmlElement,
  serializeXml,
  type XmlElement,
} from "@biblioteksentralen/xml-utils";

const fields: XmlElement[] = [
  createXmlElement("leader", { text: input.leader }),
];

const recordNode = createXmlElement("record", {
  attrs: { xmlns: "http://www.loc.gov/MARC21/slim" },
  children: fields,
});

const result = serializeXml(recordNode);