Handle Mixed-Type JSON Fields with Python Union Types

Learn how to convert JSON fields that contain different types across responses into Python Union types. Covers Union[X, Y], X | Y syntax, and type narrowing.

Advanced Patterns

Detailed Explanation

Union Types for Mixed JSON Fields

Some JSON APIs return fields that can be different types depending on context — a field might be a string in one response and an object in another. Python handles this with Union.

Example JSON (string result)

{
  "type": "simple",
  "result": "success"
}

Example JSON (object result)

{
  "type": "detailed",
  "result": {
    "status": "success",
    "count": 42
  }
}

Generated Python

from dataclasses import dataclass
from typing import Union

@dataclass
class DetailedResult:
    status: str
    count: int

@dataclass
class Response:
    type: str
    result: Union[str, DetailedResult]

Python 3.10+ Syntax

@dataclass
class Response:
    type: str
    result: str | DetailedResult

Type Narrowing

Use isinstance() to narrow the union at runtime:

if isinstance(response.result, str):
    print(response.result.upper())  # mypy knows it's str
elif isinstance(response.result, DetailedResult):
    print(response.result.count)    # mypy knows it's DetailedResult

Discriminated Unions with Literal

For cleaner narrowing, use a discriminant field:

from typing import Literal

@dataclass
class SimpleResponse:
    type: Literal["simple"]
    result: str

@dataclass
class DetailedResponse:
    type: Literal["detailed"]
    result: DetailedResult

Response = SimpleResponse | DetailedResponse

With Pydantic

Pydantic v2 supports discriminated unions natively:

from pydantic import BaseModel, Discriminator, Tag
from typing import Annotated, Union

class SimpleResponse(BaseModel):
    type: Literal["simple"]
    result: str

class DetailedResponse(BaseModel):
    type: Literal["detailed"]
    result: DetailedResult

Response = Annotated[
    Union[SimpleResponse, DetailedResponse],
    Discriminator("type"),
]

When to Use Unions

  • Polymorphic API responses — Different shapes based on a type or status field.
  • Webhook payloads — Event type determines the payload structure.
  • Flexible input — Accept multiple formats for the same field.

Use Case

You are consuming a webhook API where the payload structure varies by event type, and you need Python types that allow mypy to verify you handle every variant correctly.

Try It — JSON to Python Converter

Open full tool