Python's json Module — The Basics
Python's built-in json module handles serialization (Python → JSON) and deserialization (JSON → Python):
loads() and dumps() — String Operations
Parse and serialize JSON stringspython
1import json23# Parse a JSON string → Python dict4json_string = '{"name": "Alice", "age": 30, "active": true}'5data = json.loads(json_string)6print(data["name"]) # "Alice"7print(type(data)) # <class 'dict'>89# Serialize Python dict → JSON string10user = {"name": "Bob", "scores": [95, 87, 92], "verified": False}11json_output = json.dumps(user, indent=2)12print(json_output)13# {14# "name": "Bob",15# "scores": [95, 87, 92],16# "verified": false ← Python False becomes JSON false17# }load() and dump() — File Operations
Read and write JSON filespython
1import json2from pathlib import Path34# Read JSON from file5with open("config.json", "r", encoding="utf-8") as f:6 config = json.load(f)78# Write JSON to file9data = {"users": [{"id": 1, "name": "Alice"}]}10with open("output.json", "w", encoding="utf-8") as f:11 json.dump(data, f, indent=2, ensure_ascii=False)1213# Using pathlib (Python 3.5+)14config = json.loads(Path("config.json").read_text(encoding="utf-8"))Formatting Options
| Parameter | Purpose | Example |
|---|---|---|
| indent=2 | Pretty-print with indentation | json.dumps(data, indent=2) |
| sort_keys=True | Alphabetical key order | json.dumps(data, sort_keys=True) |
| ensure_ascii=False | Preserve Unicode (é, ñ, 中) | json.dumps(data, ensure_ascii=False) |
| separators=(",", ":") | Compact/minified output | json.dumps(data, separators=(",", ":")) |
| default=str | Serialize non-standard types | json.dumps(data, default=str) |
Handling Custom Types
Python's json module only handles basic types. For datetime, Decimal, UUID, and other types, use a custom serializer:
Custom JSON encoderpython
1import json2from datetime import datetime, date3from decimal import Decimal4from uuid import UUID56class CustomEncoder(json.JSONEncoder):7 def default(self, obj):8 if isinstance(obj, (datetime, date)):9 return obj.isoformat()10 if isinstance(obj, Decimal):11 return float(obj)12 if isinstance(obj, UUID):13 return str(obj)14 if isinstance(obj, set):15 return list(obj)16 return super().default(obj)1718order = {19 "id": UUID("12345678-1234-5678-1234-567812345678"),20 "total": Decimal("49.99"),21 "placed_at": datetime(2026, 4, 2, 14, 30),22 "tags": {"express", "priority"},23}2425print(json.dumps(order, cls=CustomEncoder, indent=2))26# {27# "id": "12345678-1234-5678-1234-567812345678",28# "total": 49.99,29# "placed_at": "2026-04-02T14:30:00",30# "tags": ["express", "priority"]31# }HTTP APIs with requests
Making API callspython
1import requests23# GET — parse JSON response4response = requests.get("https://api.github.com/users/octocat")5response.raise_for_status()6user = response.json() # Automatically parses JSON7print(user["login"]) # "octocat"89# POST — send JSON body10new_post = {"title": "Hello", "body": "World", "userId": 1}11response = requests.post(12 "https://jsonplaceholder.typicode.com/posts",13 json=new_post, # Sets Content-Type: application/json automatically14)15created = response.json()16print(created["id"]) # 101Tip
Always use the
json= parameter in requests, not data=json.dumps(). The json= parameter automatically sets the Content-Type header and handles serialization.Error handling for API responsespython
1import requests23def fetch_user(user_id: int) -> dict:4 try:5 response = requests.get(6 f"https://api.example.com/users/{user_id}",7 timeout=10,8 )9 response.raise_for_status()10 return response.json()11 except requests.exceptions.HTTPError as e:12 print(f"HTTP error: {e.response.status_code}")13 raise14 except requests.exceptions.JSONDecodeError:15 print("Response was not valid JSON")16 raise17 except requests.exceptions.ConnectionError:18 print("Could not connect to API")19 raisepandas & JSON
Reading JSON into DataFrames
Load JSON data into pandaspython
1import pandas as pd23# From a JSON file4df = pd.read_json("users.json")56# From a JSON string7json_str = '[{"name": "Alice", "score": 95}, {"name": "Bob", "score": 87}]'8df = pd.read_json(json_str)910# From an API response11df = pd.read_json("https://api.example.com/data")1213# From nested JSON — normalize nested objects14data = [15 {"name": "Alice", "address": {"city": "NYC", "zip": "10001"}},16 {"name": "Bob", "address": {"city": "LA", "zip": "90001"}},17]18df = pd.json_normalize(data)19print(df.columns)20# Index(['name', 'address.city', 'address.zip'], dtype='object')Exporting DataFrames to JSON
DataFrame to JSONpython
1import pandas as pd23df = pd.DataFrame({4 "name": ["Alice", "Bob"],5 "score": [95, 87],6 "passed": [True, True],7})89# List of objects (most common for APIs)10print(df.to_json(orient="records", indent=2))11# [12# {"name": "Alice", "score": 95, "passed": true},13# {"name": "Bob", "score": 87, "passed": true}14# ]1516# To a file17df.to_json("output.json", orient="records", indent=2)| orient | Output Shape | Best For |
|---|---|---|
| "records" | [{...}, {...}] | API responses, general use |
| "split" | {"columns": [...], "data": [...]} | Preserving column order + index |
| "index" | {"0": {...}, "1": {...}} | Row-keyed lookups |
| "columns" | {"col1": {...}, "col2": {...}} | Column-oriented analysis |
| "values" | [[...], [...]] | Compact arrays, charts |
FastAPI & Pydantic
FastAPI uses Pydantic models for automatic JSON validation, serialization, and documentation:
FastAPI with Pydantic modelspython
1from fastapi import FastAPI, HTTPException2from pydantic import BaseModel, EmailStr, Field3from datetime import datetime45app = FastAPI()67class UserCreate(BaseModel):8 name: str = Field(min_length=1, max_length=100)9 email: EmailStr10 age: int = Field(ge=0, le=150)11 role: str = Field(default="viewer", pattern="^(admin|editor|viewer)$")1213class UserResponse(BaseModel):14 id: int15 name: str16 email: str17 role: str18 created_at: datetime1920 class Config:21 from_attributes = True2223@app.post("/users", response_model=UserResponse, status_code=201)24async def create_user(user: UserCreate):25 # Pydantic already validated the JSON body26 db_user = await db.create(user.model_dump())27 return UserResponse.model_validate(db_user)2829@app.get("/users/{user_id}", response_model=UserResponse)30async def get_user(user_id: int):31 user = await db.get(user_id)32 if not user:33 raise HTTPException(status_code=404, detail="User not found")34 return UserResponse.model_validate(user)Tip
Pydantic models automatically generate JSON Schema. FastAPI uses this to build interactive API documentation at
/docs (Swagger UI) and /redoc.Pydantic for Standalone Validation
Validate JSON without FastAPIpython
1from pydantic import BaseModel, ValidationError23class Config(BaseModel):4 host: str5 port: int = 80006 debug: bool = False7 allowed_origins: list[str] = []89# Validate from dict/JSON10try:11 config = Config.model_validate_json('{"host": "localhost", "port": 3000}')12 print(config.host) # "localhost"13 print(config.debug) # False (default)14except ValidationError as e:15 print(e.errors())16 # [{"type": "missing", "loc": ["host"], "msg": "Field required"}]Command-Line JSON Processing
Python as a JSON CLI toolbash
1# Pretty-print a JSON file2python -m json.tool data.json34# Pretty-print from stdin (piped from curl)5curl -s https://api.github.com/users/octocat | python -m json.tool67# Compact/minify JSON8python -c "import json,sys; json.dump(json.load(sys.stdin), sys.stdout, separators=(',',':'))" < data.json910# Extract a field with Python one-liner11echo '{"name":"Alice","age":30}' | python -c "import json,sys; print(json.load(sys.stdin)['name'])"Common Mistakes
1. Single Quotes vs Double Quotes
1# WRONG — Python dict repr uses single quotes, which is not valid JSON2data = {'name': 'Alice'}3print(data) # {'name': 'Alice'} — this is NOT valid JSON45# CORRECT — use json.dumps() to get valid JSON6import json7print(json.dumps(data)) # {"name": "Alice"} — valid JSON2. Not Handling Encoding
1# WRONG — may fail on non-ASCII characters2with open("data.json") as f:3 data = json.load(f)45# CORRECT — always specify encoding6with open("data.json", encoding="utf-8") as f:7 data = json.load(f)3. Using eval() Instead of json.loads()
1# NEVER DO THIS — eval() executes arbitrary code2data = eval('{"name": "Alice"}') # Security vulnerability!34# ALWAYS use json.loads()5data = json.loads('{"name": "Alice"}') # SafeWarning
Using
eval() on untrusted JSON is a critical security vulnerability. An attacker could inject __import__('os').system('rm -rf /') as a value. Always use json.loads().Try It — Validate Python-Style JSON
Try It Yourself
Valid JSON that maps to a Python dict — experiment with types
Try These Tools
Continue Learning
Frequently Asked Questions
What is the difference between json.load() and json.loads() in Python?
json.load() reads from a file object (e.g., an opened file handle). json.loads() parses a JSON string directly. Similarly, json.dump() writes to a file and json.dumps() returns a string. The "s" stands for "string".
How do I pretty-print JSON in Python?
Use json.dumps(data, indent=2) to get a formatted string with 2-space indentation. Add sort_keys=True to sort keys alphabetically. For terminal output, you can also use python -m json.tool < file.json from the command line.
Can Python dictionaries be directly used as JSON?
Python dictionaries are very similar to JSON objects, but they are not the same. Python allows non-string keys, tuple values, and single quotes. JSON requires double-quoted string keys. Use json.dumps() to convert a Python dict to valid JSON, and json.loads() to convert JSON back to a dict.
How do I handle dates in JSON with Python?
JSON has no date type. Python's json module raises TypeError for datetime objects. Use a custom serializer: json.dumps(data, default=str) converts datetimes to ISO format strings. For more control, pass a custom function: default=lambda o: o.isoformat() if isinstance(o, datetime) else str(o).
How do I convert a pandas DataFrame to JSON?
Use df.to_json(orient="records") for a list of objects (most common for APIs). Other orient options: "split" (separate columns/data/index), "index" (dict keyed by index), "columns" (dict keyed by column). Add indent=2 for readable output.
What is Pydantic and why use it for JSON?
Pydantic is a Python library for data validation using type annotations. It validates JSON data, converts types automatically, and generates JSON Schema. FastAPI uses Pydantic models for request/response validation. It is the Python equivalent of Zod in TypeScript.