Skip to content

Migrating from xsdata

pyxsdata is the modernized, actively maintained successor to xsdata. It unifies formerly fragmented plugins (such as xsdata-pydantic) into a single high-performance library built exclusively for Python 3.12+.

If you are upgrading an existing project from xsdata, this guide highlights key differences and provides a step-by-step checklist.


Key Architectural Differences

Feature Legacy xsdata pyxsdata
Supported Python Python 3.8 โ€“ 3.12 Python 3.12+ exclusively
CLI Command xsdata Strictly pyxsdata
Pydantic Support External plugin (xsdata-pydantic) Built-in (pyxsdata.pydantic)
Dataclass Semantics Positional defaults (optional workarounds) Native kw_only=True everywhere
Type Annotations typing.Union, typing.Optional, typing.List X \| Y, list[T], PEP 695 Generics
Code Formatting Unformatted or black Astral Ruff (ruff>=0.9.8)
XML Parser Engines xml.etree, lxml xml.etree, lxml, C++ pugixml, and Rust PolyXML
Performance Baseline xsdata Up to 15x faster with PolyXML (~300k objs/s)
Type Checking mypy Astral ty with zero diagnostics
String Enumerations Standard Enum only Native StrEnum support (--str-enums)
Schema Defaults Replaced or discarded Preserved in metadata["default"]
Root Element Safety Silently skips mismatched root elements Strict root checking (fail_on_root_mismatch)

Migration Checklist

1. Update Dependencies

Remove xsdata and xsdata-pydantic from your pyproject.toml or requirements.txt, and replace them with pyxsdata:

$ uv remove xsdata xsdata-pydantic
$ uv add "pyxsdata[cli,pydantic,core]"
$ pip uninstall xsdata xsdata-pydantic
$ pip install "pyxsdata[cli,pydantic,core]"

2. Update Import Statements

Replace all references to the xsdata namespace with pyxsdata:

# Before
from xsdata.formats.dataclass.parsers import XmlParser
from xsdata.formats.dataclass.serializers import XmlSerializer
from xsdata_pydantic.bindings import XmlParser as PydanticXmlParser

# After
from pyxsdata.formats.dataclass.parsers import XmlParser
from pyxsdata.formats.dataclass.serializers import XmlSerializer
from pyxsdata.pydantic.bindings import XmlParser as PydanticXmlParser

3. Update CLI Commands

The CLI command is strictly pyxsdata:

# Before
$ xsdata generate schema.xsd --output pydantic

# After
$ pyxsdata generate schema.xsd --output pydantic

!!! note "Zero Backwards Compatibility Shims" pyxsdata intentionally does not install an xsdata executable alias to ensure clear separation and prevent ambiguous behavior in multi-package environments.

4. Benefit from Python 3.12+ kw_only=True

In older versions of xsdata, if a required XML element followed an optional element, Python's @dataclass would raise a TypeError: non-default argument follows default argument. Older solutions forced non-nullable fields to be typed as Optional[T] = None.

In pyxsdata, all generated dataclasses use kw_only=True by default:

@dataclass(kw_only=True)
class Person:
    middle_name: str | None = None  # optional with default
    last_name: str  # required non-default field works cleanly!

This guarantees that required schema elements remain strictly required in Python.

5. Take Advantage of PolyXML & pugixml Acceleration

pyxsdata offers next-generation parsing backends for applications handling high-throughput XML feeds, message queues, or massive bulk files:

Option A: Native Rust Core Engine (PolyXML)

The core extra leverages PolyXML, a zero-copy Rust extension that bypasses intermediate Python DOM trees and instantiates models directly via C-API at ~300,000+ objects/sec (up to 15x faster than legacy xsdata):

$ uv add "pyxsdata[core]"

Use CoreXmlParser directly:

from pyxsdata.formats.dataclass.parsers import CoreXmlParser

parser = CoreXmlParser()
data = parser.parse("huge_feed.xml", FeedModel)

Or pass handler=CoreEventHandler:

from pyxsdata.formats.dataclass.parsers import XmlParser
from pyxsdata.formats.dataclass.parsers.handlers import CoreEventHandler

parser = XmlParser(handler=CoreEventHandler)
data = parser.parse("huge_feed.xml", FeedModel)

Works identically with Pydantic v2 via from pyxsdata.pydantic.bindings import CoreXmlParser or CoreXmlSerializer!

Option B: C++ Streaming (pugixml)

If you require constant-memory C++ pull-parsing, install the pugixml extra:

$ uv add "pyxsdata[pugixml]"

And switch your parser handler:

from pyxsdata.formats.dataclass.parsers import XmlParser
from pyxsdata.formats.dataclass.parsers.handlers import PugixmlEventHandler

parser = XmlParser(handler=PugixmlEventHandler)
data = parser.parse("huge_feed.xml", FeedModel)

Read more in the Parser Backends Guide.

6. Faster Deserialization Out of the Box

pyxsdata includes built-in optimizations that make XML deserialization up to 54% faster (over 2x throughput) than legacy xsdata:

  • Direct Scalar Type Fast-Paths: Bypasses general converter dispatch and exception handling for single str, int, and float candidate types, accelerating primitive scalar conversions by up to 2.7x.
  • ProxyConverter Fast-Path: Calls target factory callables directly for XmlDate, XmlTime, XmlDateTime, XmlDuration, and XmlPeriod without allocating keyword argument dictionaries.
  • Cached Schema Metadata & Tuple Iteration: Caches XmlMeta.get_children resolution as immutable tuples, eliminating hundreds of thousands of generator allocations and iter() call overheads.
  • Fast-Path Primitive Node Instantiation: Immediately constructs PrimitiveNode for scalar fields without querying XSI attributes or factory classes.
  • Static Method Binding: Uses static methods for field binding to avoid per-call bound method descriptor construction.
  • Slice-Free Intermediate Object Processing: Traverses queued object tuples using direct indexing instead of allocating intermediate sublists during object binding.
  • Zero-Cost Converter Dispatch & Single-Type Fast-Path: Replaced expensive exception-suppression wrappers with direct dictionary lookups.
  • MRO Converter Caching: Fast-paths class inheritance lookups by caching converter resolution directly in the type registry.
  • Parser Node Caching: Avoids repeated module imports in parsing hot paths.
  • Short-Circuited XSI Attribute Checks: Instantly skips XSI type and nil checks when elements carry no attributes.

7. Modern StrEnum Generation

With Python 3.12+ as the baseline, pyxsdata supports generating enum.StrEnum instead of standard Enum for string-based enumerations:

$ pyxsdata generate schema.xsd --str-enums

Generated StrEnum subclasses can be used directly as strings across your codebase without manual .value unwrapping.

8. Strict Root Element Validation

Legacy xsdata silently skipped parsing or returned empty models if the document's root XML tag didn't match the model class. In pyxsdata, you can enable strict root element validation using fail_on_root_mismatch:

from pyxsdata.formats.dataclass.parsers import XmlParser
from pyxsdata.formats.dataclass.parsers.config import ParserConfig

config = ParserConfig(fail_on_root_mismatch=True)
parser = XmlParser(config=config)

9. Preserved Schema Defaults in Metadata

When generating models where optional fields receive None defaults in Python, pyxsdata preserves the original XML Schema default value in field metadata={"default": ...} for seamless introspection and downstream schema validation.


๐Ÿš€ Moving to PolyXML (Next-Generation XML Architecture)

If you are beginning a greenfield project, require cross-language data contracts, or need extreme parsing performance, we strongly recommend evaluating PolyXML.

Why PolyXML?

Dimension pyxsdata PolyXML
Language Ecosystem Python 3.12+ exclusively 7 Languages (Python, Rust, C++20, Java 21+, TypeScript, Go, C# 12)
Compiler Engine Python AST code generator Single-binary safe Rust compiler (protoc for XML)
Parsing Engine Python interpreter loop / C++ / Rust hook Zero-allocation monomorphized Rust streaming runtime
Throughput & Latency ~300k objs/s with pyxsdata[core] 10xโ€“24x faster than pure Python, 3.2 ยตs telemetry packet latency
Security (XXE / SSRF) Relies on backend flags (e.g. lxml config) Structurally immune by design (pure Rust, zero filesystem/network I/O)
JSON Interchange Separate serializers / JSON converters Zero-copy streaming XML โ†” JSON transcoder (polyxml transcode)

When to stay with pyxsdata:

  • Existing codebases heavily integrated with xsdata's legacy handler architecture, custom plugins, or WSDL/SOAP webservices.
  • Workflows that require fine-grained dynamic AST manipulation in pure Python.

When to migrate to PolyXML:

  • Projects processing gigabytes of XML data or high-frequency telemetry where Python CPU overhead is a bottleneck.
  • Polyglot organizations needing identical, synchronized schema data contracts across multiple services (e.g. Python microservice + Rust engine + Java backend + TypeScript UI).
  • Environments requiring rock-solid, zero-configuration security against XXE attacks.

๐Ÿ‘‰ Explore the PolyXML Documentation & Quickstart โ†’