dapper91 / pydantic-xml

python xml for humans
https://pydantic-xml.readthedocs.io
The Unlicense
141 stars 14 forks source link

Is it possible to skip/ignore irrelevant fields in a model? #181

Closed phdowling closed 2 months ago

phdowling commented 2 months ago

I would like to define a model that only partially parses/validates XML according to fields that I care about, ignoring the rest. If the irrelevant fields come later on in the model, I can use extra="ignore"to accomplish this. However, I have not found a way to skip some fields at the beginning of the model, or intermediate fields (i.e. read field1 and field3, ignoring the fact that field2 comes inbetween).

Minimal example:


from pydantic_xml import BaseXmlModel, element

class MyModel1(BaseXmlModel, tag="model", extra="ignore"):
    field1: str = element()  # this works, because field2 just gets ignored
    # field2: str = element()

class MyModel2(BaseXmlModel, tag="model", extra="ignore"):
    # field1: str = element()  # this breaks, because the model expects field2 as the first field to parse!
    field2: str = element()

class MyModel3(BaseXmlModel, tag="model", extra="ignore"):
    field1: str = element()
    # field2: str = element()  # this also breaks
    field3: str = element()

xml = """<?xml version="1.0" encoding="UTF-8"?>
<model>
    <field1>1</field1>
    <field2>2</field2>
    <field3>3</field3>
</model>"""

m = MyModel1.from_xml(xml)
print(m)

m = MyModel2.from_xml(xml) 
# pydantic_core._pydantic_core.ValidationError: 1 validation error for MyModel2
# field2
#   [line -1]: Field required [type=missing, input_value={}, input_type=dict]
print(m)

m = MyModel3.from_xml(xml)
# pydantic_core._pydantic_core.ValidationError: 1 validation error for MyModel3
# field3
#   [line -1]: Field required [type=missing, input_value={'field1': '1'}, input_type=dict]
print(m)

´´´
phdowling commented 2 months ago

Solved using https://pydantic-xml.readthedocs.io/en/latest/pages/data-binding/elements.html#elements-search-mode

e.g.

from pydantic_xml import BaseXmlModel, element
from pydantic_xml.element import SearchMode

mode = SearchMode.UNORDERED  # SearchMode.ORDERED also happens to work here

class MyModel1(BaseXmlModel, tag="model", extra="ignore", search_mode=mode):
    field1: str = element()
    # field2: str = element()

class MyModel2(BaseXmlModel, tag="model", extra="ignore", search_mode=mode):
    # field1: str = element()
    field2: str = element()

class MyModel3(BaseXmlModel, tag="model", extra="ignore", search_mode=mode):
    field1: str = element()
    # field2: str = element()
    field3: str = element()

xml = """<?xml version="1.0" encoding="UTF-8"?>
<model>
    <field1>1</field1>
    <field2>2</field2>
    <field3>3</field3>
</model>"""

m = MyModel1.from_xml(xml)
print(m)

m = MyModel2.from_xml(xml)
print(m)

m = MyModel3.from_xml(xml)
print(m)