Generic models#
pydantic
library supports generic-models.
Generic xml model can be declared the same way:
Model
AuthType = TypeVar('AuthType')
class Request(BaseXmlModel, Generic[AuthType], tag='request'):
request_id: UUID = attr(name='id')
timestamp: float = attr()
auth: AuthType
class BasicAuth(BaseXmlModel):
user: str = attr()
password: SecretStr = attr()
class TokenAuth(BaseXmlModel):
token: UUID = element()
BasicRequest = Request[BasicAuth]
TokenRequest = Request[TokenAuth]
Document
<request id="27765d90-f3ef-426f-be9d-8da2b405b4a9"
timestamp="1674976874.291046">
<auth user="root" password="secret"/>
</request>
{
"request_id": "27765d90-f3ef-426f-be9d-8da2b405b4a9",
"timestamp": 1674976874.291046,
"auth": {
"user": "root",
"password": "secret"
}
}
<request id="27765d90-f3ef-426f-be9d-8da2b405b4a9"
timestamp="1674976874.291046">
<auth>
<token>7de9e375-84c1-441f-a628-dbaf5017e94f</token>
</auth>
</request>
{
"request_id": "27765d90-f3ef-426f-be9d-8da2b405b4a9",
"timestamp": 1674976874.291046,
"auth": {
"token": "7de9e375-84c1-441f-a628-dbaf5017e94f"
}
}
A generic model can be of one or more types and organized in a recursive structure. The following example illustrate how to describes a flexable SOAP request model:
model.py:
import datetime as dt
import pathlib
from typing import Generic, TypeVar
from pydantic import HttpUrl
from pydantic_xml import BaseXmlModel, element
AuthType = TypeVar('AuthType')
class SoapHeader(
BaseXmlModel, Generic[AuthType],
tag='Header',
ns='soap',
):
auth: AuthType
class SoapMethod(BaseXmlModel):
pass
MethodType = TypeVar('MethodType', bound=SoapMethod)
class SoapBody(
BaseXmlModel, Generic[MethodType],
tag='Body',
ns='soap',
):
call: MethodType
HeaderType = TypeVar('HeaderType', bound=SoapHeader)
BodyType = TypeVar('BodyType', bound=SoapBody)
class SoapEnvelope(
BaseXmlModel,
Generic[HeaderType, BodyType],
tag='Envelope',
ns='soap',
nsmap={
'soap': 'http://www.w3.org/2003/05/soap-envelope/',
},
):
header: HeaderType
body: BodyType
class BasicAuth(
BaseXmlModel,
tag='BasicAuth',
ns='auth',
nsmap={
'auth': 'http://www.company.com/auth',
},
):
user: str = element(tag='Username')
password: str = element(tag='Password')
class CreateCompanyMethod(
SoapMethod,
tag='CreateCompany',
ns='co',
nsmap={
'co': 'https://www.company.com/co',
},
):
trade_name: str = element(tag='TradeName')
founded: dt.date = element(tag='Founded')
website: HttpUrl = element(tag='WebSite')
CreateCompanyRequest = SoapEnvelope[
SoapHeader[
BasicAuth
],
SoapBody[
CreateCompanyMethod
],
]
xml_doc = pathlib.Path('./doc.xml').read_text()
request = CreateCompanyRequest.from_xml(xml_doc)
json_doc = pathlib.Path('./doc.json').read_text()
assert request == CreateCompanyRequest.model_validate_json(json_doc)
doc.xml:
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/">
<soap:Header>
<auth:BasicAuth xmlns:auth="http://www.company.com/auth">
<auth:Username>admin</auth:Username>
<auth:Password>secret</auth:Password>
</auth:BasicAuth>
</soap:Header>
<soap:Body>
<co:CreateCompany xmlns:co="https://www.company.com/co">
<co:TradeName>SpaceX</co:TradeName>
<co:Founded>2002-03-14</co:Founded>
<co:WebSite>https://www.spacex.com</co:WebSite>
</co:CreateCompany>
</soap:Body>
</soap:Envelope>
doc.json:
{
"header": {
"auth": {
"user": "admin",
"password": "secret"
}
},
"body": {
"call": {
"trade_name": "SpaceX",
"founded": "2002-03-14",
"website": "https://www.spacex.com"
}
}
}