Python Basics — Working with JSON Files

2 minute read

Working with JSON Files

JSON (JavaScript Object Notation) is the most popular format for web APIs, configuration files, and data exchange. Python’s built-in json module handles it effortlessly.

Python ↔ JSON Type Mapping

Python JSON
dict object {}
list, tuple array []
str string ""
int, float number
True / False true / false
None null

Reading JSON Files — json.load()

import json

with open("config.json", "r") as f:
    data = json.load(f)

print(type(data))   # <class 'dict'>
print(data["name"]) # Access like a normal Python dict

Sample config.json:

{
    "name": "MyApp",
    "version": "2.1.0",
    "debug": false,
    "features": ["auth", "logging", "cache"],
    "database": {
        "host": "localhost",
        "port": 5432
    }
}

Parsing JSON Strings — json.loads()

import json

json_string = '{"name": "Alice", "age": 30, "active": true}'
data = json.loads(json_string)

print(data["name"])   # Alice
print(data["active"]) # True (Python bool)

load() reads from a file, loads() reads from a string (the “s” stands for “string”).

Writing JSON Files — json.dump()

import json

config = {
    "name": "MyApp",
    "version": "2.1.0",
    "debug": False,
    "features": ["auth", "logging"],
}

with open("config.json", "w") as f:
    json.dump(config, f)

Converting to JSON Strings — json.dumps()

import json

data = {"name": "Alice", "scores": [95, 87, 92]}

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

Pretty-Printing JSON

import json

data = {
    "users": [
        {"name": "Alice", "role": "admin"},
        {"name": "Bob", "role": "user"},
    ],
    "settings": {"theme": "dark", "language": "en"},
}

# Pretty-print to string
pretty = json.dumps(data, indent=4)
print(pretty)

# Pretty-print to file
with open("output.json", "w") as f:
    json.dump(data, f, indent=4)

Output:

{
    "users": [
        {
            "name": "Alice",
            "role": "admin"
        },
        {
            "name": "Bob",
            "role": "user"
        }
    ],
    "settings": {
        "theme": "dark",
        "language": "en"
    }
}

Sorting Keys

import json

data = {"zebra": 1, "apple": 2, "mango": 3}
print(json.dumps(data, indent=2, sort_keys=True))
{
  "apple": 2,
  "mango": 3,
  "zebra": 1
}

Working with API Response Data

import json
from urllib.request import urlopen

def fetch_user(user_id):
    """Fetch user data from a JSON API."""
    url = f"https://jsonplaceholder.typicode.com/users/{user_id}"
    
    with urlopen(url) as response:
        data = json.loads(response.read().decode())
    
    return data

user = fetch_user(1)
print(f"Name: {user['name']}")
print(f"Email: {user['email']}")
print(f"City: {user['address']['city']}")

Handling JSON Errors

import json

# Invalid JSON
bad_json = "{'name': 'Alice'}"  # Single quotes are not valid JSON

try:
    data = json.loads(bad_json)
except json.JSONDecodeError as e:
    print(f"JSON Error: {e}")
    # JSON Error: Expecting property name enclosed in double quotes

Common Patterns

Read, Modify, Write Back

import json

# Read
with open("settings.json", "r") as f:
    settings = json.load(f)

# Modify
settings["theme"] = "dark"
settings["last_updated"] = "2025-03-03"

# Write back
with open("settings.json", "w") as f:
    json.dump(settings, f, indent=4)

Default Values with .get()

import json

with open("config.json", "r") as f:
    config = json.load(f)

# Safe access with defaults
debug = config.get("debug", False)
port = config.get("port", 8080)
name = config.get("name", "Unknown")

Handling Non-Serializable Types

import json
from datetime import datetime

data = {
    "event": "login",
    "timestamp": datetime.now(),  # Not JSON-serializable!
}

# ❌ This fails:
# json.dumps(data)  # TypeError: Object of type datetime is not JSON serializable

# ✅ Convert manually:
data["timestamp"] = data["timestamp"].isoformat()
print(json.dumps(data, indent=2))

# ✅ Or use a custom encoder:
class DateEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

data = {"event": "login", "timestamp": datetime.now()}
print(json.dumps(data, cls=DateEncoder, indent=2))

JSON Quick Reference

Operation Code
Read from file json.load(f)
Parse from string json.loads(s)
Write to file json.dump(data, f)
Convert to string json.dumps(data)
Pretty-print json.dumps(data, indent=4)
Sort keys json.dumps(data, sort_keys=True)
Handle errors except json.JSONDecodeError

Summary

  • json.load() / json.dump() work with files
  • json.loads() / json.dumps() work with strings
  • Use indent=4 for human-readable output
  • Use .get() for safe access with defaults
  • Handle datetime and other non-serializable types with custom encoders
  • Always wrap JSON parsing in try/except for untrusted data

Next up: File Handling Exercises →

Updated: