Quickstart

pyxsd maps an XML document into a Python object tree according to its XML Schema (XSD), reports validation issues, runs user-defined transforms, and writes the tree back out as XML. The base install’s runtime dependencies are elementpath (XSD regular expressions and XPath) and defusedxml, both pure Python with no compiled extensions. defusedxml is loaded only when an opt-in parser profile selects it; the default parser keeps the stdlib’s speed and compatibility.

Requirements

  • Python 3.11 or newer (3.11–3.15 tested)

  • Two pure-Python dependencies: elementpath (XSD regular expressions) and defusedxml (opt-in parser hardening); no compiled extensions

Note

Parsing untrusted XML is a decision, not a default. See XML security and untrusted input for XmlParserPolicy, XmlLimits, remote schema rules, and the XPath/DoS boundary.

Installation

pip install pyxsd

or with uv:

uv tool install pyxsd

Command line

Given a schema and an instance document:

pyxsd --inputXml inventory.xml

pyxsd locates the schema from the instance’s xsi:schemaLocation-style hints (or you can pass -s schema.xsd), builds the object tree, and validates it. By default nothing is written: a bare run is a validation pass. Pass -k to write the parsed tree, or give a transform with -t; transform output goes to standard output by default. Use -o FILE (or -o stdout) to choose the destination explicitly, or -d for the default filename (<input>Transformed.xml). Useful flags:

# Validate strictly: exit 1 if the report contains errors
pyxsd -i inventory.xml -s schema.xsd --strict

# Write the parsed tree to a file, then apply a transform
pyxsd -i inventory.xml -s schema.xsd -k -o transformed.xml -t 'PrintData()'

# Chain transforms with '>'
pyxsd -i inventory.xml -t 'PrintData() > PrintData()'

See Command-line reference for the complete flag reference and Transforms for transform syntax.

Library use

Two objects do the work: a compiled Schema and the Document instances it produces. Compiling the schema builds the Python classes; each parse binds one instance document:

import pyxsd

schema = pyxsd.Schema.compile("inventory.xsd")
schema.require_valid()  # raises pyxsd.ValidationError if the schema is bad

document = schema.parse("inventory.xml")
document.require_valid()  # raises pyxsd.ValidationError if the document is bad

A failed check raises ValidationError, whose report attribute holds every issue found so far. Without it, the document keeps going — pyxsd is a lax validator — and the report is there when you want it:

if document.report.has_errors:
    for issue in document.report.issues:
        print(issue.format())

document.root is the parsed tree’s root instance. Transforms are plain callables that take the root; one returning a tree root comes back as a new Document you can write out:

def normalize_units(root): ...


updated = document.transform(normalize_units)
updated.write("normalized.xml")

A transform that changes the tree’s shape leaves the report describing the tree as it was parsed; call updated.revalidate() to re-parse the serialized tree and get a fresh report for its current shape. The tree walks with pyxsd.tree.iter_tree or the walk/visitor helpers in The Transform class; the node shape is documented in Data model.

What pyxsd validates

pyxsd is a lax validator for recoverable issues: it builds the tree even when the document has validation errors and records non-fatal issues in a Validation report rather than aborting. The supported feature set includes the built-in type lattice, sequence / choice / all content models, groups, wildcards, substitution groups, xsi:type dispatch, nil/default/fixed, identity constraints, and schema composition. Known gaps are tabulated in Supported features.

Next steps