pydantic-xml

repository·master·Indexed 42 days ago

https://github.com/dapper91/pydantic-xml

A Pydantic extension for seamless XML serialization and deserialization by binding XML elements and attributes to Pydantic models. It supports namespaces, generic models for flexible structures like SOAP requests, and various search modes (strict, ordered, unordered) for parsing XML documents.

Tokens
15.6K
Snippets
71
Records
89
Agent score
54%

What's inside pydantic-xml

  1. Overview of pydantic-xml

    master
    pydantic-xml is an extension for pydantic that provides XML binding for model fields, as well as XML serialization and deserialization. Because it is closely integrated with Pydantic, it supports most Pydantic features including support for both Pydantic v1 and v2, generic models, and computed fields.
  2. Bind mapping types to attributes or elements

    master

    Fields using mapping types (e.g., Dict[str, str], Mapping[str, int], TypedDict) are bound to local element attributes by default. To bind them to sub-elements instead, mark the field with @element().

    Default (Attributes)

    class Metadata(BaseXmlModel):
        tags: dict[str, str]  # <metadata tags="key1:val1,key2:val2" />

    Using @element

    class Metadata(BaseXmlModel):
        tags: dict[str, str] = element()  # <metadata><tags>...</tags></metadata>
    from pydantic_xml import BaseXmlModel, element
    from typing import Dict
    
    class Metadata(BaseXmlModel):
        tags: Dict[str, str] = element()
  3. Implement Discriminated Unions using XML attributes

    master

    While Pydantic uses tagged unions based on field values, pydantic-xml supports a similar mechanism to distinguish between sub-models based on an XML attribute value. This allows the parser to select the correct model type by inspecting a specific attribute on the XML element.

    from typing import Union, Literal
    from pydantic_xml import BaseXMLModel, attr
    
    class TypeA(BaseXMLModel):
        kind: Literal['a'] = attr()
        value: str
    
    class TypeB(BaseXMLModel):
        kind: Literal['b'] = attr()
        value: int
    
    class Container(BaseXMLModel):
        item: Union[TypeA, TypeB]
  4. Configure default namespaces

    master

    You can set a default namespace for a model and its sub-fields using the nsmap parameter in the BaseXmlModel declaration. To set a default namespace, pass the namespace URI with an empty string key ('').

    Note on Serialization: The standard library XML serializer may struggle with default namespaces, often substituting them with ns0, ns1, etc. To ensure namespace declarations remain untouched and respect the hierarchy, it is highly recommended to use the lxml backend.

    from pydantic_xml import BaseXmlModel, element
    
    class Company(BaseXmlModel, nsmap={'': 'http://www.company.com/co'}):
        name: str = element()
  5. How Union types work in pydantic-xml

    master

    To declare a field that can be one of several types, use typing.Union. This works for both primitive types (text, attributes, elements) and complex models.

    Important Constraints:

    • You cannot combine primitive types and models within the same Union (e.g., Union[str, MyModel] is not supported).
    • The order of types in the declaration matters because validation follows specific modes.

    Validation Modes: pydantic-xml relies on Pydantic's validation logic, which supports:

    • left_to_right: The first type that successfully validates is accepted.
    • smart (default): The first type that matches without coercion wins.
  6. Create nested wrappers for complex XML structures

    master

    You can nest wrappers by applying wrapped to a field that is itself defined using wrapped. This is particularly useful for extracting data from sub-elements that reside in different namespaces or deep, non-linear structures, allowing you to build a logical model that doesn't strictly mirror the physical XML hierarchy.

    from pydantic_xml import BaseXmlModel, wrapped
    
    class InnerModel(BaseXmlModel):
        value: str
    
    class OuterModel(BaseXmlModel):
        # A wrapper that points to a path, which contains another wrapped element
        nested_data: wrapped[InnerModel, "level1/level2"]
  7. Inherit namespaces for attributes from the model

    master

    By default, attributes require an explicit ns parameter if they are namespaced. To allow attributes to automatically inherit the namespace defined at the model level, set the ns_attrs=True flag in the model configuration.

    from pydantic_xml import BaseXmlModel, attr
    
    class Product(BaseXmlModel):
        namespaces = {'p': 'http://product.com'}
        ns_attrs = True
    
        # Because ns_attrs=True, this inherits the 'p' namespace
        sku: str = attr(name='sku')

    Resulting XML:

    <Product xmlns:p="http://product.com" p:sku="ABC-123" />
  8. Bind primitive types to XML attributes or elements

    master

    By default, fields with primitive types (e.g., int, float, str, datetime) are bound to the element's text content. You can override this default behavior using specific decorators:

    1. Attributes: Use @attr to bind a primitive field to an XML attribute.
    2. Elements: Use @element to bind a primitive field to a sub-element.

    Default (Text Content)

    class User(BaseXmlModel):
        name: str  # <user>name_value</user>

    Using @attr

    class User(BaseXmlModel):
        name: str = attr()  # <user name="name_value" />

    Using @element

    class User(BaseXmlModel):
        name: str = element()  # <user><name>name_value</name></user>
    from pydantic_xml import BaseXmlModel, attr, element
    
    class User(BaseXmlModel):
        name: str = attr()
    
    class Profile(BaseXmlModel):
        bio: str = element()
  9. Declare Namespaces for Root Models

    master

    You can define namespaces for a root model by setting the ns (the namespace alias) and nsmap (the namespace mapping) parameters. The namespace mapping defined at the root level is automatically inherited by all field models within that hierarchy.

    from pydantic_xml import BaseXmlModel
    
    class Model(BaseXmlModel, tag="model", ns="ns", nsmap={"ns": "http://example.com"}):
        pass
  10. Handle heterogeneous collections with predefined element counts

    master
    Heterogeneous collections are used when the number of elements is predefined and each element follows specific, potentially different, binding rules. Unlike homogeneous collections where elements are of the same type and follow the same rules, heterogeneous collections allow for fixed-length structures where each position can have its own schema. The most common example of a heterogeneous collection is typing.Tuple (e.g., Tuple[int, int, int]).