Elements#

Primitive types#

A field of a primitive type marked as pydantic_xml.element() is bound to the sub-element text. Parameter tag is used to declare the sub-element tag to which the field is bound. If it is omitted the field name is used (respecting pydantic field aliases).

Model
class Company(BaseXmlModel, tag='company'):
    founded: dt.date = element()
    website: HttpUrl = element(tag='web-size')
Document
<company>
    <founded>2002-03-14</founded>
    <web-size>https://www.spacex.com</web-size>
</company>
{
    "founded": "2002-03-14",
    "website": "https://www.spacex.com"
}

Model types#

A field of a model type marked as pydantic_xml.element() is bound to a sub-element. Then the sub-element is used as the root for that sub-model. For more information see model data binding. Parameter tag is used to declare a sub-element tag to which the sub-model is bound. If it is omitted the sub-model tag setting is used. If it is omitted too the field name is used (respecting pydantic field aliases). So the order is the following: element tag, model tag, field alias, field name.

Model
class Headquarters(BaseXmlModel, tag='headquarters'):
    country: str = element()
    state: str = element()
    city: str = element()


class Company(BaseXmlModel, tag='company'):
    headquarters: Headquarters
Document
<company>
    <headquarters>
        <country>US</country>
        <state>California</state>
        <city>Hawthorne</city>
    </headquarters>
</company>
{
    "headquarters": {
        "country": "US",
        "state": "California",
        "city": "Hawthorne"
    }
}

Namespaces#

You can declare the element namespace passing parameters ns and nsmap to pydantic_xml.element() where ns is the element namespace alias and nsmap is a namespace mapping:

Model
class Company(BaseXmlModel, tag='company'):
    founded: dt.date = element(
        ns='co',
        nsmap={'co': 'http://www.company.com/co'},
    )
    website: HttpUrl = element(tag='web-size')
Document
<company>
    <co:founded xmlns:co="http://www.company.com/co">2002-03-14</co:founded>
    <web-size>https://www.spacex.com</web-size>
</company>
{
    "founded": "2002-03-14",
    "website": "https://www.spacex.com"
}

The namespace and namespace mapping can be declared for a model. In that case all fields except attributes inherit them:

Model
class Company(
    BaseXmlModel,
    tag='company',
    ns='co',
    nsmap={'co': 'http://www.company.com/co'},
):
    founded: dt.date = element()
    website: HttpUrl = element(tag='web-size', ns='co')
Document
<co:company xmlns:co="http://www.company.com/co">
    <co:founded>2002-03-14</co:founded>
    <co:web-size>https://www.spacex.com</co:web-size>
</co:company>
{
    "founded": "2002-03-14",
    "website": "https://www.spacex.com"
}

The namespace and namespace mapping can be also applied to model types passing ns and nsmap to pydantic_xml.element(). If they are omitted the model namespace and namespace mapping is used:

Model
class Headquarters(
    BaseXmlModel,
    tag='headquarters',
    ns='hq',
    nsmap={'hq': 'http://www.company.com/hq'},
):
    country: str = element(ns='hq')
    state: str = element(ns='hq')
    city: str = element(ns='hq')


class Company(BaseXmlModel, tag='company'):
    headquarters: Headquarters
Document
<company>
    <hq:headquarters xmlns:hq="http://www.company.com/hq">
        <hq:country>US</hq:country>
        <hq:state>California</hq:state>
        <hq:city>Hawthorne</hq:city>
    </hq:headquarters>
</company>
{
    "headquarters": {
        "country": "US",
        "state": "California",
        "city": "Hawthorne"
    }
}

Elements search mode#

A model supports several element search strategies (modes). Each strategy has its own pros and cons.

Strict (default)#

The element to which a field will be bound is searched sequentially one by one (without skipping unknown elements). If the tag of a next element doesn’t match the field tag that field is considered unbound. This mode is used when the strong document validation is required. If you parse a large document it is the best choice because it works in predictable time since it doesn’t require any look-ahead operations.

Model
class Company(
    BaseXmlModel,
    tag='Company',
    search_mode='strict',
):
    founded: str = element(tag='Founded')
    website: str = element(tag='WebSite')

Error

code raises an exception because of the incorrect field order

Document
<Company>
    <WebSite>https://www.spacex.com</WebSite>
    <Founded>2002-03-14</Founded>
</Company>
{}

Ordered#

The element to which a field will be bound is searched sequentially skipping unknown elements. If the tag of the next element doesn’t match the field tag that element is skipped and the search continues. This mode is used when the elements order matters but unexpected (or irrelevant) elements could appear in a document.

Model
class Company(
    BaseXmlModel,
    tag='Company',
    search_mode='ordered',
):
    founded: str = element(tag='Founded')
    website: str = element(tag='WebSite')
Document
<Company>
    <Founded>2002-03-14</Founded>
    <Founder name="Elon" surname="Musk"/>
    <WebSite>https://www.spacex.com</WebSite>
</Company>
{
    "founded": "2002-03-14",
    "website": "https://www.spacex.com"
}

Warning

This mode could lead to some unexpected results. For example the following model:

class Model(BaseXmlModel, search_mode='ordered'):
    field1: Optional[str] = element(tag='element1')
    field2: str = element(tag='element2')
    field3: str = element(tag='element1')

fails for the following document:

<Model>
    <element2>value</element2>
    <element1>value</element2>
</Model>

because the first field will be bound to the second element (the algorithm looks ahead until the first match found, which is the second element) and the second field will not be bound to any element.

Unordered#

The element to which a field will be bound is searched among all sub-elements in any order. This mode is used when the elements order doesn’t matter. The time complexity of this strategy in worst case is O(F*E) where F - is the number of fields, E - the number of sub-elements so that it is not suitable for large documents.

Model
class Company(
    BaseXmlModel,
    tag='Company',
    search_mode='unordered',
):
    founded: str = element(tag='Founded')
    website: str = element(tag='WebSite')
Document
<Company>
    <WebSite>https://www.spacex.com</WebSite>
    <Founded>2002-03-14</Founded>
</Company>
{
    "founded": "2002-03-14",
    "website": "https://www.spacex.com"
}