Pydantic XML Extension
Pydantic-xml is a Pydantic extension that provides XML binding for model fields, enabling seamless XML serialization and deserialization. It is deeply integrated with Pydantic, supporting most of its features. The library is actively maintained, with frequent releases addressing bug fixes and new features, often several times a month.
Warnings
- breaking Migration to Pydantic V2 (from Pydantic V1) requires significant changes. `pydantic-xml` 2.x supports Pydantic V2+. While `pydantic-xml` itself aims for compatibility, direct Pydantic V1 (`pydantic.v1.BaseModel`) and Pydantic V2 (`pydantic.BaseModel`) models cannot be mixed directly within the same hierarchy.
- breaking Since v2.16.0, an exception is now raised if a namespace alias used in a model or field is not found within the model's namespace map (`__xml_nsmap__`). This changed behavior from silently ignoring missing aliases.
- breaking The way custom root types are declared changed significantly around `pydantic-xml` 2.0.0. Custom root models must now inherit from `RootXmlModel` instead of `BaseXmlModel` with `__root__` attribute.
- gotcha By default, `pydantic-xml` maps Python field names directly to XML element/attribute names. If the XML tag or attribute name differs (e.g., due to different casing or hyphens), `ParsingError` will be raised unless `tag` or `name` arguments are explicitly provided to `element()`, `attr()`, or `BaseXmlModel`.
- gotcha The syntax for defining custom field serializers and validators has evolved with Pydantic V2. While older `@xml_field_serializer` and `@xml_field_validator` decorators are still functional, the `Annotated` pattern with `pydantic.field_serializer` and `pydantic.field_validator` (or `pydantic.BeforeValidator`, `pydantic.AfterValidator`, etc.) is the recommended Pydantic V2 approach for better integration and type safety.
- breaking The encoding format for `bool` and `None` types changed. `bool` values now serialize as lowercase `'true'` or `'false'` instead of title-case `'True'` or `'False'`. `None` values now serialize as an empty string `''` instead of `'None'`.
Install
-
pip install pydantic-xml -
pip install pydantic-xml[lxml]
Imports
- BaseXmlModel
from pydantic_xml import BaseXmlModel
- RootXmlModel
from pydantic_xml import RootXmlModel
- attr
from pydantic_xml import attr
- element
from pydantic_xml import element
- wrapped
from pydantic_xml import wrapped
Quickstart
from typing import List, Optional
from pydantic import HttpUrl
from pydantic_xml import BaseXmlModel, attr, element
class Product(BaseXmlModel):
status: str = attr() # e.g., 'running', 'development'
launched: Optional[int] = attr(default=None)
title: str # Extracted from the element text
class Company(BaseXmlModel, tag='Company'):
trade_name: str = attr(name='trade-name')
website: HttpUrl = element()
products: List[Product] = element(tag='product', default_factory=list)
xml_doc = """
<Company trade-name="SpaceX">
<website>https://www.spacex.com</website>
<product status="running" launched="2013">Several launch vehicles</product>
<product status="running" launched="2019">Starlink</product>
<product status="development">Starship</product>
</Company>
"""
# Deserialize XML to a Pydantic model
company = Company.from_xml(xml_doc)
print(f"Company: {company.trade_name}, Website: {company.website}")
for product in company.products:
print(f" Product: {product.title}, Status: {product.status}, Launched: {product.launched}")
# Serialize Pydantic model back to XML
new_xml_doc = company.to_xml(pretty_print=True)
print("\nSerialized XML:")
print(new_xml_doc.decode())