将 pydantic 与 xml 一起使用

Asy*_*llz 6 python xml pydantic

我正在开发一个使用大量 xml 的项目,并且想使用 pydantic 来建模对象。在本例中,我简化了 xml,但包含了一个示例对象。

<ns:SomeType name="NameType" shortDescription="some data">
  <ns:Bar
    thingOne="alpha"
    thingTwo="beta"
    thingThree="foobar"/>
</ns:SomeType>
Run Code Online (Sandbox Code Playgroud)

代码

from pydantic import BaseModel
from typing import Optional, List
from xml.etree import ElementTree as ET


class Bar(BaseModel):
  thing_one: str
  thing_two: str
  thing_three: str


class SomeType(BaseModel):
  name: str
  short_description: str
  bar: Optional[Bar]


def main():
  with open("path/to/file.xml") as fp:
    source = fp.read()
  root = ET.fromstring(source)
  some_type_list = []
  for child in root:
    st = SomeType(
      name=child.attrib["name"],
      short_description=child.attrib["shortDescription"],
    )
    for sub in child:
      st.bar = Bar(
        thing_one=sub.attrib["thingOne"],
        thing_two=sub.attrib["thingTwo"],
        thing_three=sub.attrib["thingThree"],
      )
Run Code Online (Sandbox Code Playgroud)

我研究了 BaseModel.parse_obj 或 BaseModel.parse_raw 但我认为这不能解决问题。我还认为我可以尝试使用 xmltodict 来转换 xml,命名空间和 @ 属性会更加妨碍......

>>> import xmltodict
>>> xmltodict.parse(input_xml)
{'ns:SomeType': {'@name': 'NameType', '@shortDescription': 'some data', ... }}
Run Code Online (Sandbox Code Playgroud)

Her*_*cón 6

xmltodict如果将其与字段别名结合起来,可以在您的示例中提供帮助:

from typing import Optional

import xmltodict
from pydantic import BaseModel, Field


class Bar(BaseModel):
    thing_one: str = Field(alias="@thingOne")
    thing_two: str = Field(alias="@thingTwo")
    thing_three: str = Field(alias="@thingThree")


class SomeType(BaseModel):
    name: str = Field(alias="@name")
    short_description: str = Field(alias="@shortDescription")
    bar: Optional[Bar] = Field(alias="ns:Bar")


class Root(BaseModel):
    some_type: SomeType = Field(alias="ns:SomeType")


print(
    Root.model_validate(
        xmltodict.parse(
            """<ns:SomeType name="NameType" shortDescription="some data">
  <ns:Bar
    thingOne="alpha"
    thingTwo="beta"
    thingThree="foobar"/>
</ns:SomeType>""")).some_type)
Run Code Online (Sandbox Code Playgroud)

输出:

name='NameType' short_description='some data' bar=Bar(thing_one='alpha', thing_two='beta', thing_three='foobar')
Run Code Online (Sandbox Code Playgroud)

您可以在上面的示例中看到,Root需要一个模型,因为字典有一个ns:SomeType键。