--- title: Parsing JSON into Specific Pydantic Models 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. One use case I have at the moment is to parse a json object and build different objects depending on some 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"] subject: Literal["map", "layer", "feature"] metadata: Optional[dict] = None key: Optional[str] = None class PeerMessage(BaseModel): kind: Literal["peermessage"] = "peermessage" sender: str recipient: str message: dict class ServerRequest(BaseModel): kind: Literal["server"] = "server" action: Literal["list-peers"] ``` Each of these classes share the same `kind` property, which can act as a 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[ServerRequest, PeerMessage, OperationMessage] = Field( discriminator="kind" ) ``` Which can be used this way: ```python try: incoming = Request.model_validate_json(raw_message) except ValidationError as e: # raise ``` And, 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) ```