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, and C++ pugixml
Performance Baseline xsdata Up to 54% faster deserialization (2x throughput)
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]"
$ pip uninstall xsdata xsdata-pydantic
$ pip install "pyxsdata[cli,pydantic]"

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 pugixml

If you parse large XML feeds or high-throughput API responses, 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.