dapper91 / pydantic-xml

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

Validation error when model doesn't include all fields #108

Closed enadeau closed 10 months ago

enadeau commented 10 months ago

Hi first time user of that package so maybe I'm doing something wrong but this looks a lot like a bug to me.

When running the following snippet I get a validation error saying the field yy is missing but it is clearly part of the document.

import pydantic_xml

class Foo(pydantic_xml.BaseXmlModel):
    # xx: str = pydantic_xml.element()
    yy: str = pydantic_xml.element()

    # model_config = {"extra": "forbid"}

data = """<?xml version="1.0" encoding="UTF-8"?>
   <Foo>
      <xx>hkjh</xx>
      <yy>jhkjh</yy>
   </Foo>
"""

res = Foo.from_xml(data)
print(res)

If I uncomment the field xx then the error goes away.

If I forbid extra then it complains about the xx and the yy field being extra.

Running on 3.11.4 with the following package installed:

annotated-types==0.5.0
anyio==4.0.0
black==23.7.0
certifi==2023.7.22
click==8.1.7
h11==0.14.0
httpcore==0.17.3
httpx==0.24.1
idna==3.4
mypy==1.5.1
mypy-extensions==1.0.0
packaging==23.1
pathspec==0.11.2
platformdirs==3.10.0
pydantic==2.3.0
pydantic-xml==2.2.0
pydantic_core==2.6.3
ruff==0.0.287
sniffio==1.3.0
typing_extensions==4.7.1
enadeau commented 10 months ago

Of course I only needed 5 minutes after writing the issue to figure out that I wanted to use the search mode parameter to achieve the behavior I was looking for.

I would suggest to maybe have this in the quick start guide. I think for many user coming from pydantic it is an unexpected behavior.

Maybe could be part if the error message as well but that might be more complicated as they come from pydantic core

dapper91 commented 10 months ago

Hi @enadeau

The model uses strict element search mode by default which means the element to which a field will be bound is searched sequentially without skipping unknown elements.

More info here

This should fix your problem:

>>> import pydantic_xml
>>>
>>>
>>> class Foo(pydantic_xml.BaseXmlModel, search_mode='ordered'):
...     yy: str = pydantic_xml.element()
...
>>>
>>> data = """<?xml version="1.0" encoding="UTF-8"?>
...    <Foo>
...       <xx>hkjh</xx>
...       <yy>jhkjh</yy>
...    </Foo>
... """
>>>
>>> res = Foo.from_xml(data.encode())
>>> print(res)
yy='jhkjh'
enadeau commented 10 months ago

Got it thanks for your help