Python JSON Handling — Core Concepts

While the Python json module covers the standard library API, this article focuses on practical JSON handling patterns — the workflows you use when building real applications.

Reading JSON

From a String

import json

raw = '{"name": "Alice", "age": 30, "skills": ["Python", "SQL"]}'
data = json.loads(raw)
# data is a Python dict: {'name': 'Alice', 'age': 30, 'skills': ['Python', 'SQL']}

From a File

with open("config.json", encoding="utf-8") as f:
    config = json.load(f)

From an API Response

Most HTTP libraries handle JSON parsing for you:

import httpx

response = httpx.get("https://api.example.com/users/1")
user = response.json()  # Parses JSON automatically

Writing JSON

To a String

data = {"name": "Alice", "scores": [95, 87, 92]}
json_text = json.dumps(data)
# '{"name": "Alice", "scores": [95, 87, 92]}'

Pretty Printing

json_text = json.dumps(data, indent=2)

To a File

with open("output.json", "w", encoding="utf-8") as f:
    json.dump(data, f, indent=2, ensure_ascii=False)

The ensure_ascii=False flag keeps non-ASCII characters readable instead of escaping them as \uXXXX.

Type Mapping

JSON types map to Python types:

JSONPython
{} objectdict
[] arraylist
"string"str
123 / 1.5int / float
true / falseTrue / False
nullNone

Notice what’s missing: dates, sets, tuples, bytes, custom objects. These need special handling.

Handling Non-Serializable Types

The most common error is TypeError: Object of type X is not JSON serializable.

Custom Encoder

import json
from datetime import datetime, date
from decimal import Decimal

def json_default(obj):
    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    if isinstance(obj, Decimal):
        return str(obj)
    if isinstance(obj, set):
        return list(obj)
    raise TypeError(f"Object of type {type(obj)} is not JSON serializable")

data = {"created": datetime.now(), "price": Decimal("19.99"), "tags": {"python", "json"}}
json.dumps(data, default=json_default)

Dataclass Serialization

from dataclasses import dataclass, asdict

@dataclass
class User:
    name: str
    age: int

user = User("Alice", 30)
json.dumps(asdict(user))

Safe JSON Parsing

Handling Malformed Input

def safe_parse(raw: str, default=None):
    try:
        return json.loads(raw)
    except json.JSONDecodeError as e:
        print(f"Invalid JSON at line {e.lineno}, col {e.colno}: {e.msg}")
        return default

Extracting Nested Values Safely

Deep JSON structures often have optional fields:

def get_nested(data: dict, *keys, default=None):
    current = data
    for key in keys:
        if isinstance(current, dict):
            current = current.get(key)
        elif isinstance(current, list) and isinstance(key, int):
            current = current[key] if key < len(current) else None
        else:
            return default
        if current is None:
            return default
    return current

# Usage
user = {"profile": {"address": {"city": "London"}}}
city = get_nested(user, "profile", "address", "city")  # "London"
missing = get_nested(user, "profile", "phone", default="N/A")  # "N/A"

JSON Lines (JSONL)

For streaming or log-style data, JSON Lines uses one JSON object per line:

# Reading JSONL
with open("events.jsonl", encoding="utf-8") as f:
    events = [json.loads(line) for line in f if line.strip()]

# Writing JSONL
with open("events.jsonl", "w", encoding="utf-8") as f:
    for event in events:
        f.write(json.dumps(event) + "\n")

JSONL is easier to stream, append to, and process line by line than regular JSON arrays.

Common Misconception

“JSON supports comments.” Standard JSON has no comment syntax. If you need comments in configuration, use TOML or YAML. Some parsers (like JSON5) extend JSON with comments, but they’re not standard.

One Thing to Remember

JSON handling in Python goes beyond loads/dumps — production code needs custom serializers for dates and decimals, safe nested access for API responses, and JSONL format for streaming data.

pythonjsondata-processingtext-processingapis

See Also

  • Python Csv Processing Learn how Python reads and writes spreadsheet-style CSV files — the universal language of data tables.
  • Python Template Strings See how Python's Template strings let you fill in blanks safely, like a Mad Libs game that can't go wrong.
  • Python Toml Configuration Discover TOML — the config file format Python chose for its own projects, designed to be obvious and impossible to mess up.
  • Ci Cd Why big apps can ship updates every day without turning your phone into a glitchy mess — CI/CD is the behind-the-scenes quality gate and delivery truck.
  • Containerization Why does software that works on your computer break on everyone else's? Containers fix that — and they're why Netflix can deploy 100 updates a day without the site going down.