Working with JSON in Python: A Complete Guide
Quick answer: Python’s json module provides simple methods to parse JSON strings (json.loads()), read JSON files (json.load()), and write JSON data (json.dumps() and json.dump()).
What Is JSON?
JSON (JavaScript Object Notation) is a lightweight, text-based format for storing and transmitting data. It’s the universal standard for data exchange across APIs, configuration files, and databases.
JSON structure:
- Data organized in key-value pairs
- Keys must be strings
- Values can be strings, numbers, booleans, arrays, objects, or null
Example JSON array:
json
[
{
"name": "John",
"age": 17
},
{
"name": "Alice",
"age": 24
}
]
Reading JSON in Python
From a JSON String
When you get JSON from an API, it’s usually a string:
python
import json
import requests
response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
todo_string = response.text
# Convert string to Python dictionary
todo_dict = json.loads(todo_string)
print(todo_dict["id"]) # 1
print(todo_dict["title"]) # delectus aut autem
From a JSON File
When data comes from a .json file:
python
import json
with open("todos.json", "r") as file:
todos = json.load(file) # Returns Python list/dict
Modifying JSON Data
Once parsed, JSON becomes regular Python data structures you can manipulate:
python
def capitalize_title(todo):
todo["title"] = todo["title"].capitalize()
return todo
# Modify the dictionary
todo_dict_capitalized = capitalize_title(todo_dict)
Writing JSON in Python
To a JSON String
Convert Python objects back to JSON strings:
python
# Convert dictionary to JSON string
todo_json = json.dumps(todo_dict_capitalized)
print(todo_json) # {"userId": 1, "id": 1, "title": "Delectus aut autem", "completed": false}
# Pretty-print with indentation
todo_json_pretty = json.dumps(todo_dict_capitalized, indent=4)
print(todo_json_pretty)
To a JSON File
Write Python data directly to a file:
python
with open("capitalized_todos.json", "w") as file:
json.dump(todos, file, indent=4)
Working with Nested JSON
Access deeply nested data safely:
python
data = {
"user": {
"profile": {
"name": "Alice",
"age": 30
}
}
}
# Using square brackets
name = data["user"]["profile"]["name"]
# Using .get() (safer for missing keys)
age = data.get("user", {}).get("profile", {}).get("age")
For deeply nested or unpredictable structures, use a recursive function:
python
def find_key(data, target):
if isinstance(data, dict):
if target in data:
return data[target]
for value in data.values():
result = find_key(value, target)
if result:
return result
elif isinstance(data, list):
for item in data:
result = find_key(item, target)
if result:
return result
return None
# Usage
email = find_key(complex_data, "email")
Custom Objects with JSON
Decoding JSON to Custom Objects
Use object_hook to convert JSON to your own classes:
python
class Todo:
def __init__(self, user_id, id, title, completed):
self.user_id = user_id
self.id = id
self.title = title
self.completed = completed
def capitalize_title(self):
self.title = self.title.capitalize()
return self
@classmethod
def from_dict(cls, data):
return cls(
user_id=data["userId"],
id=data["id"],
title=data["title"],
completed=data["completed"]
)
# Load JSON directly into Todo objects
with open("todos.json", "r") as file:
todos = json.load(file, object_hook=Todo.from_dict)
# Now you can use class methods
for todo in todos:
todo.capitalize_title()
Encoding Custom Objects to JSON
Create a custom encoder:
python
class TodoEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Todo):
return {
"userId": obj.user_id,
"id": obj.id,
"title": obj.title,
"completed": obj.completed
}
return super().default(obj)
# Write custom objects to JSON file
with open("todos_export.json", "w") as file:
json.dump(todos, file, cls=TodoEncoder, indent=4)
Pretty-Printing JSON
Make JSON human-readable:
python
import json
ugly_json = '{"userId":1,"id":1,"title":"delectus aut autem","completed":false}'
parsed = json.loads(ugly_json)
# Basic pretty print
print(json.dumps(parsed, indent=4))
# Custom formatting
print(json.dumps(
parsed,
indent=4,
separators=(", ", ": "),
sort_keys=True
))
Key Functions Comparison
| Function | Purpose | Input | Output |
|---|---|---|---|
json.load() | Read JSON file | File object | Python object |
json.loads() | Parse JSON string | String | Python object |
json.dump() | Write to JSON file | Python object + file | None (writes file) |
json.dumps() | Convert to JSON string | Python object | JSON string |
Common JSON to Python Type Mapping
| JSON Type | Python Type |
|---|---|
| Object | dict |
| Array | list |
| String | str |
| Number | int or float |
| Boolean | bool |
| Null | None |
Error Handling
Always handle JSON parsing errors:
python
import json
try:
with open("data.json", "r") as file:
data = json.load(file)
except FileNotFoundError:
print("File not found")
except json.JSONDecodeError as e:
print(f"Invalid JSON: {e}")
Summary
| Task | Method |
|---|---|
| Parse JSON string → Python dict | json.loads() |
| Read JSON file → Python object | json.load() |
| Convert Python dict → JSON string | json.dumps() |
| Write Python object → JSON file | json.dump() |
| Custom object decoding | object_hook parameter |
| Custom object encoding | Subclass JSONEncoder |
| Pretty print | indent parameter |
| Handle missing keys | .get() or recursive search |
Python’s json module makes working with JSON data straightforward—whether you’re consuming APIs, reading configuration files, or exporting data for other applications.