pydantic-xml icon indicating copy to clipboard operation
pydantic-xml copied to clipboard

Using elements as discriminators

Open JoepdeJong opened this issue 3 months ago • 2 comments
trafficstars

I am currently working with an XML schema where the discriminators are passed as an element, instead of an attribute. For example, the following schema, where type is the discriminator.

<Zoo>
  <Animals>
    <Cat>
      <type>cat</type>
      <name>Whiskers</name>
      <lives>9</lives>
    </Cat>
    <Dog>
      <type>dog</type>
      <name>Rex</name>
      <breed>Labrador</breed>
    </Dog>
  </Animals>
</Zoo>

I defined the following models:


class Cat(BaseXmlModel, tag="Cat"):
    type: Literal["cat"] = element(name="type")  # discriminator
    name: str = element()
    lives: int = element()

class Dog(BaseXmlModel, tag="Dog"):
    type: Literal["dog"] = element(name="type")  # discriminator
    name: str = element()
    breed: str = element()

Pet = Annotated[Union[Cat, Dog], Field(discriminator="type")]

# --- Container with list of union ---
class Zoo(BaseXmlModel, tag="Zoo"):
    animals: List[Pet] = wrapped("Animals", element())

However, running

zoo = Zoo.from_xml(xml_data)
print(zoo)

leads to the following error:

pydantic_xml.errors.ModelFieldError: Zoo.animals field type error: discriminator field must be an xml attribute

Is there another way to use elements as discriminators?

JoepdeJong avatar Aug 21 '25 15:08 JoepdeJong

@JoepdeJong Hi,

right now there is no way to use an element text as a union discriminator.

dapper91 avatar Sep 13 '25 19:09 dapper91

In your case as I can see non-discriminated unions must behave the same as discriminated ones

class Cat(BaseXmlModel, tag="Cat"):
    type: Literal["cat"] = element(name="type")  # discriminator
    name: str = element()
    lives: int = element()

class Dog(BaseXmlModel, tag="Dog"):
    type: Literal["dog"] = element(name="type")  # discriminator
    name: str = element()
    breed: str = element()

# --- Container with list of union ---
class Zoo(BaseXmlModel, tag="Zoo"):
    animals: List[Union[Cat, Dog]] = wrapped("Animals", element())

dapper91 avatar Sep 13 '25 19:09 dapper91