--- title: Discriminate pydantic objects by field tags: pydantic, python, match --- I really like [Pydantic](https://docs.pydantic.dev/latest/) because it makes it easy to define the the structure of the objects I want to use, using typing. I wanted to parse a json object and build a different object depending on the value of some specific key in the JSON object. Here, I have three types of messages: `OperationMessage`, `PeerMessage` and `ServerRequest`, as follows: ```python from typing import Literal, Optional from pydantic import BaseModel class OperationMessage(BaseModel): kind: Literal["operation"] = "operation" verb: Literal["upsert", "update", "delete"] class PeerMessage(BaseModel): kind: Literal["peermessage"] = "peermessage" sender: str recipient: str message: dict class ServerMessage(BaseModel): kind: Literal["server"] = "server" action: Literal["list-peers"] ``` Each of these classes share the same `kind` key, but each of them has a different value for it. it's our discriminator. Let's build a generic `Request` class that will be able to build for me the proper objects: ```python from typing import Union from pydantic import Field, RootModel, ValidationError class Request(RootModel): root: Union[ServerMessage, PeerMessage, OperationMessage] = Field( discriminator="kind" ) ``` Which can be used this way: ```python try: incoming = Request.model_validate_json(raw_message) except ValidationError as e: # Oh noes. ``` Because we have classes, we can leverage the `match` statement: ```python match incoming.root: case OperationMessage(): # Here to broadcast the message websockets.broadcast(peers, raw_message) case PeerMessage(recipient=_id): # Or to send peer messages to the proper peer peer = connections.get(_id) if peer: await peer.send(raw_message) # ... Etc. ```