Programming / Python
Tutorial

Python Tutorial: Parse and Validate JSON with Pydantic

Use Pydantic to turn raw JSON into typed Python models, catch malformed payloads early, and verify input before it reaches production logic.

Python Tutorial: Parse and Validate JSON with Pydantic

Why this workflow matters

JSON is often the first untrusted input your Python code sees. In APIs, automation jobs, log pipelines, and security tooling, the payload may be missing required fields, contain the wrong types, or include values that look valid but break downstream logic. If you parse JSON with plain dictionaries and skip validation, those problems usually surface later as runtime failures, bad decisions, or silent data corruption.

This tutorial shows how to parse and validate JSON with Pydantic so you can turn raw input into typed Python objects with explicit validation rules. By the end, you will be able to decide whether Pydantic is the right fit, build a small validation model, process JSON safely, and verify the result before production use.

Stop here if your input is not trusted JSON

Before you start, confirm that the data you are validating is actually JSON and not an arbitrary string format, a CSV variant, or a partially structured log line. Pydantic validates Python data structures and JSON documents after they are parsed, but it does not replace safe input handling at the boundary.

Stop here if any of the following are true:

  • You do not control the input source and cannot tolerate malformed payloads without a fallback path.
  • The payload may be huge enough to create memory pressure when decoded.
  • You need to validate against a policy that depends on external systems, such as live certificate status or directory lookups.
  • The input is not JSON at all and needs a different parser first.

If your workflow includes HTTP APIs, secure transport and response integrity still matter before validation. For example, if the JSON comes from a remote service, combine this approach with secure TLS verification in Python Requests so you are validating data that arrived over a trusted channel.

What you will build

You will build a small validation layer that accepts raw JSON, converts it into a Pydantic model, and rejects invalid payloads with useful error messages. The finished state should look like this:

  1. A model that defines the expected JSON structure.
  2. A parsing step that loads raw JSON into Python data.
  3. A validation step that enforces types, required fields, and value constraints.
  4. A predictable error path for rejected payloads.
  5. A few checks you can run before deploying the validator into a service, script, or pipeline.

Prerequisites and preparation

Goal

Make sure your Python environment is ready and that you know where JSON enters your application.

Action

Install Pydantic in a virtual environment and identify one representative payload you want to validate.

python -m venv .venv
source .venv/bin/activate
pip install pydantic

Create a sample JSON document that represents the shape you expect in production.

{
  "event_id": "evt-1001",
  "source": "sensor-a",
  "severity": 3,
  "active": true
}

Expected output

You have a clean environment and a realistic sample payload that reflects your operational data.

Validation

Confirm the package is installed and importable:

python -c "import pydantic; print(pydantic.__version__)"

Common failure

  • Using a system Python without isolation and breaking other projects.
  • Validating a toy example that does not match real field names or data types.
  • Assuming the payload shape is stable when upstream producers actually vary by version or environment.

Define the validation model

Goal

Describe the expected JSON structure in code so invalid data is rejected consistently.

Action

Create a Pydantic model with typed fields and constraints that match your input contract.

from pydantic import BaseModel, Field


class Event(BaseModel):
    event_id: str = Field(min_length=1)
    source: str = Field(min_length=1)
    severity: int = Field(ge=0, le=5)
    active: bool

This model says the JSON must contain four fields:

  • event_id and source must be non-empty strings.
  • severity must be an integer between 0 and 5.
  • active must be a boolean.

Expected output

You now have an explicit contract for the JSON structure instead of relying on dictionary keys scattered throughout the codebase.

Validation

Check that the model accepts a valid object:

from pydantic import ValidationError

payload = {
    "event_id": "evt-1001",
    "source": "sensor-a",
    "severity": 3,
    "active": True,
}

try:
    event = Event.model_validate(payload)
    print(event)
except ValidationError as exc:
    print(exc)

Common failure

  • Using the wrong type annotations and expecting Pydantic to infer a stricter rule than it actually does.
  • Forgetting constraints such as range checks, which allows semantically invalid values through.
  • Treating optionality as implicit when the field is actually required.

Parse raw JSON into a validated object

Goal

Convert a JSON string into a typed Pydantic object in one predictable step.

Action

Use model_validate_json() when you receive raw JSON text.

from pydantic import BaseModel, Field, ValidationError


class Event(BaseModel):
    event_id: str = Field(min_length=1)
    source: str = Field(min_length=1)
    severity: int = Field(ge=0, le=5)
    active: bool


raw_json = '{"event_id":"evt-1001","source":"sensor-a","severity":3,"active":true}'

try:
    event = Event.model_validate_json(raw_json)
    print(event)
    print(event.model_dump())
except ValidationError as exc:
    print(exc)

The object returned by Pydantic is safe to use in downstream logic because it has already passed the model rules.

If your JSON arrives as a file, socket stream, or message body, decode it into text only if your input handling is already safe. For data extracted from unstructured logs, validate the extraction pattern first; otherwise, the JSON validator may only expose a bad parse step that should have been prevented earlier. In those cases, regex validation for log parsing and data extraction can help you verify the upstream extraction boundary.

Expected output

A valid payload becomes an Event instance, and the same fields can be emitted again with model_dump() for further processing.

Validation

Run a known-good payload and confirm:

  • The object prints as an Event instance.
  • model_dump() returns a dictionary with the expected keys and values.
  • No coercion surprises appear in fields you intended to keep strict.

Common failure

  • Passing malformed JSON text and assuming it will be treated as a validation error instead of a parse error.
  • Accidentally validating a Python dictionary when the interface really receives a JSON string.
  • Forgetting that certain values may be coerced unless you use stricter field types.

Handle invalid JSON and validation errors

Goal

Reject bad payloads with actionable error details instead of letting the failure surface later.

Action

Test both syntax errors and schema errors so you know which layer failed.

from pydantic import BaseModel, Field, ValidationError


class Event(BaseModel):
    event_id: str = Field(min_length=1)
    source: str = Field(min_length=1)
    severity: int = Field(ge=0, le=5)
    active: bool


examples = [
    '{"event_id":"evt-1001","source":"sensor-a","severity":3,"active":true}',
    '{"event_id":"","source":"sensor-a","severity":9,"active":true}',
    '{"event_id":"evt-1002","source":"sensor-b","severity":2,"active":tru}',
]

for raw_json in examples:
    try:
        event = Event.model_validate_json(raw_json)
        print("accepted:", event.model_dump())
    except ValidationError as exc:
        print("rejected:")
        print(exc)

Expected output

The valid payload is accepted. The invalid data is rejected with details that point to the field or JSON location causing the problem.

Validation

Confirm that you can distinguish at least these two cases:

  • A schema violation such as an out-of-range severity value.
  • Malformed JSON syntax such as a truncated boolean or missing delimiter.

Common failure

  • Returning a generic 500 error when the issue is really bad input.
  • Logging raw payloads without considering whether they contain sensitive data.
  • Overwriting validation errors with custom code that hides the original field-level detail.

Add strictness where operationally important

Goal

Prevent unsafe coercion in fields where exact type matching matters.

Action

Use stricter field definitions when a value must not be quietly converted from a near match.

from pydantic import BaseModel, Field, StrictBool, StrictInt, StrictStr


class StrictEvent(BaseModel):
    event_id: StrictStr = Field(min_length=1)
    source: StrictStr = Field(min_length=1)
    severity: StrictInt = Field(ge=0, le=5)
    active: StrictBool

Strict fields are useful when you process security-relevant events, configuration files, or automation input where accepting `

Use this guidance together with Python thread-safe logging and Python asyncio subprocess management to connect the workflow with related operational context already available on the site.

Part of the Programming: Python Insights content cluster.

Continue learning

Related content