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:
| JSON | Python |
|---|---|
{} object | dict |
[] array | list |
"string" | str |
123 / 1.5 | int / float |
true / false | True / False |
null | None |
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.
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.