Skip to content

Getting Started

Installation

xml-model requires Zod v4 as a peer dependency.

bash
npm install xml-model zod

No special TypeScript compiler plugins or tsconfig.json changes are required.

First model

Define a class by extending the result of xmlModel(). Pass a Zod schema where fields are plain Zod types by default (child elements), xml.attr() for XML attributes, or xml.prop() when you need to customise a field's tagname, inline mode, or matching. Supply a { tagname } option for the root element name.

ts
/**
 * A car engine. Extends `XMLBase` so unknown vendor elements inside `<engine>`
 * (e.g. manufacturer extensions) survive a read-modify-write cycle.
 * Demonstrates a basic nested class with one XML attribute (`type`) and one
 * child element (`horsepower`).
 */
export class Engine extends XMLBase.extend(
  {
    /** Fuel type stored as an XML attribute: `<engine type="petrol">` */
    type: xml.attr(z.string()),
    /** Power output stored as a child element: `<horsepower>150</horsepower>` */
    horsepower: z.number(),
  },
  xml.root({ tagname: "engine" }),
) {}
ts
// XML → instance
const engine = Engine.fromXML(`<engine type="petrol"><horsepower>150</horsepower></engine>`);
engine.type; // "petrol"
engine.horsepower; // 150
engine instanceof Engine; // true

// instance → XML string
Engine.toXMLString(engine);
// <engine type="petrol"><horsepower>150</horsepower></engine>

Class methods

Any methods you define on the class are available on parsed instances:

ts
/**
 * Base vehicle class. Extends `XMLBase` so all vehicle subclasses inherit
 * round-trip preservation — element order and unknown extensions are kept
 * intact without any per-subclass ceremony.
 * Demonstrates `xml.attr()` for identifier fields and custom instance methods.
 */
export class Vehicle extends XMLBase.extend(
  {
    /** Unique identifier stored as a root XML attribute: `<vehicle vin="...">` */
    vin: xml.attr(z.string()),
    /** Manufacturer name stored as a child element: `<make>Toyota</make>` */
    make: z.string(),
    /** Production year stored as a child element: `<year>2020</year>` */
    year: z.number(),
  },
  xml.root({ tagname: "vehicle" }),
) {
  /** Returns a human-readable label for this vehicle. */
  label() {
    return `${this.year} ${this.make}`;
  }
}
ts
const vehicle = Vehicle.fromXML(
  `<vehicle vin="V001"><make>Toyota</make><year>2020</year></vehicle>`,
);
vehicle.label(); // "2020 Toyota"

Next steps

  • Models — class extension, nested models, dataSchema, schema()
  • Propertiesxml.prop(), xml.attr(), arrays, optional fields