dapper91 / pydantic-xml

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

Annotated attributes not serializing correctly. #180

Closed Woody1193 closed 4 days ago

Woody1193 commented 2 months ago

I have a field with an optional decimal.Decimal which was causing some issues with Pydantic. The fix I found was to use Annotated to apply constraints to the decimal.Decimal instead of to the type union:

from decimal import Decimal
from pydantic_xml import BaseXmlModel, attr
from typing import Annotated

class Test(BaseXmlModel):

     foo: Annotated[Decimal, attr(name="Foo", ge=0.00, lt=10000.00, decimal_places=2)] | None = None

The XML conversion here succeeds but produces malformed output:

t = Test(foo=25.54)
t.to_xml(skip_empty=True, encoding="utf-8")  # Produces b'<Test>25.54</Test>' but should have produced b'<Test Foo="25.54"/>'

This appears to be an issue with how I made the field optional as if I modify Test like this:

class Test(BaseXmlModel):

     foo: Annotated[Decimal, attr(name="Foo", ge=0.00, lt=10000.00, decimal_places=2)]

then the XML conversion produces the expected output.

I'm either misunderstanding this XML, misusing the package or have stumbled upon a bug.

dapper91 commented 2 months ago

@Woody1193 Hi,

Unfortunately, annotated attributes are not supported right now. The workaround I came up with is:

>>> from decimal import Decimal
>>> from pydantic import Field
>>> from pydantic_xml import BaseXmlModel, attr
>>> from typing import Annotated
>>>
>>>
>>> class Test(BaseXmlModel):
...      foo: Annotated[Decimal, Field(ge=0.00, lt=10000.00, decimal_places=2)] | None = attr(name="Foo", default=None)
...
>>>
>>> t = Test(foo=25.54)
>>> print(t.to_xml(skip_empty=True, encoding="utf-8"))
b'<Test Foo="25.54" />'