Why pyxsd¶
pyxsd is built for applications that need to work with schema-defined XML as Python data. Compile an XSD once, then parse documents into typed objects, inspect validation findings, and use the same bound tree for queries, edits, and transforms. The schema supplies the structure; your Python code supplies the application behavior.
Let the schema build the classes¶
Schema.compile() creates Python classes in memory from XSD declarations.
Schema extensions become Python inheritance, declared elements and
attributes get descriptors, and simple values use XSD datatype classes.
You can reuse that compiled schema across documents without a code-generation
command, generated modules to check in, or a second model definition to keep
in sync with the XSD.
This matters when you consume upstream schemas, handle several schema versions, or want to start processing a new vocabulary immediately. Recompile the chosen schema to build its bindings. Changes can still require changes to application code, but there is no generated-source refresh step.
For editor support, Schema.stub() emits optional .pyi descriptions of
the runtime classes. Stubs supplement the bindings; they do not implement
them. See Type stubs for generated binding classes for typing limits and Architecture for the
compiler model.
Validate XSD 1.1 with published evidence¶
pyxsd defaults to XSD 1.1, including assertions and conditional type assignment, and offers an explicit XSD 1.0 mode. You can use those schema rules in the same Python workflow as the bindings. See Supported features for the implemented constructs and remaining gaps.
The W3C test-suite results are published from reviewed baselines:
Raw checked-case agreement for pyxsd against the pinned main suite.xml
corpus:
Profile |
Passed |
Checked |
Agreement on checked |
|---|---|---|---|
XSD 1.1 |
41,530 |
41,605 |
99.8197% |
XSD 1.0 |
39,306 |
39,438 |
99.6653% |
Checked cases are pass + fail; agreement is 100 * pass / (pass + fail).
Main-suite cases only: extra-suite.xml is excluded, and no combined
XSD 1.0/1.1 rate is reported. See
XSTS methodology and residuals.
These figures measure validity-verdict agreement, not certification or complete standards coverage. XSTS methodology and results identifies the pinned corpus, non-verdict outcomes, residual failures, and reproduction commands, so you can inspect the evidence behind the result.
Keep validation and application work together¶
A parsed Document carries both its bound tree and a ValidationReport.
Recoverable findings accumulate with stable codes and schema/instance phase
tags. Call require_valid() where your application needs to reject errors,
or inspect the report to decide what to repair. Unreadable or malformed XML
can still stop a run before a document is returned.
Binding retention is a separate choice. Lax retention can keep invalid lexical values as raw text and undeclared content as generic nodes while validation findings remain visible. That is useful for import review and repair tools: inspecting the bad data need not mean suppressing its errors. Retained trees are best effort, not a lossless source copy. See Parse modes and binding policies.
Findings with source locations identify indexed occurrences, so repeated
siblings can be distinguished even when their values are identical.
Document.locate() connects a finding to a bound node or its source-only
occurrence when available. Location precision is explicit; missing evidence
is not filled with guessed line numbers. See Validation.
Query and extend the objects you already have¶
find(), findall(), and xpath() return the original bound nodes for
element results. You can select an inventory item, read its typed quantity,
and pass that object to ordinary Python functions. Callable transforms work
on the same model; after editing, revalidate() returns a freshly checked
document for writing. The Quickstart walks through that complete path.
Mixin extensions add your methods to selected schema-generated classes at
compile time. A __pyxsd_validate__ hook can contribute application findings
to the document’s report alongside XSD findings. You keep handwritten
behavior separate from the schema-derived structure, without editing
generated files. See Mixin extensions and Using transforms.
Import and export JSON using Parker, BadgerFish,
JsonML, or pyxsd’s own formats. Native pyxsd keeps the dictionary
layout; pyxsd-json uses the XSD to choose scalar, object, and array layouts
while retaining namespaces, child order, and mixed text positions. Generate
JSON Schema for any of these formats directly from XSD. See
Data conversion for examples and limits.
Derive application models and tables from XSD¶
Optional Pydantic and Arrow integrations prepare models and record schemas from the compiled XSD before instance XML is available. An empty record selection still has a declared Arrow schema; column types do not depend on which values happen to appear in the first file.
Use Pydantic snapshots for Python/JSON application boundaries, or select records for Arrow tables and Parquet. Named columns can include values from the record’s ancestors or document root, such as an order number beside each line item. These are explicit projections with supported-shape limits, not lossless XML codecs or substitutes for full XML validation. See Optional Pydantic and Arrow integrations and Data workflow examples.
Use it in Python, pipelines, and agent tools¶
The base library is pure Python with two dependencies, elementpath and
defusedxml; it needs no compiled extension. Optional integrations bring
their own dependencies. The CLI exposes validation, transforms, and stub
emission, while the optional local MCP server adds schema exploration,
source-linked diagnostics, queries, and exports for agent clients. See
Command-line reference and MCP integration.
Choose for your workflow¶
Choose pyxsd when you want the XSD to drive a Python object model and the work that follows validation. Start with Quickstart, or see how pyxsd compares with xmlschema, lxml, and libxml2.
Check the boundaries before adopting it: pyxsd builds a full instance tree,
parses one document at a time per compiled Schema, and requires a schema
for Python/JSON-to-XML conversion. Some parsed complex-child descriptors
require document queries. Namespace-aware binding
is the default with ParseModes.NAMESPACED; explicit ParseModes.STRICT,
ParseModes.LAX, or --namespaces legacy restores local-name matching. For
untrusted input, select parser protections and resource rules explicitly using
XML security and untrusted input; local-only loading is not filesystem confinement.