How to Parse JSON in Python: Read, Write & Work With JSON Files
Python

Nerijus Kriaučiūnas
Key Takeaways
-
Python’s JSON module makes it easy to convert between JSON and native data types.
-
Custom encoders and hooks simplify working with complex objects.
-
Pretty-printing and nested parsing make JSON data easier to read.
JSON is the universal standard for exchanging textual data. You can encounter it in virtually any part of modern codebases, whether frontend, backend, APIs, or databases.
In this article, you’ll learn how to read, modify, and write JSON with Python.
What Is JSON?
JSON (JavaScript Object Notation) is a lightweight, text-based format for storing and transmitting data.
In JSON data, information is organized in key-value pairs. Keys must be strings, while values can be in any of the supported types .
These key-value pairs can be further grouped into JSON objects and JSON arrays, which are essential parts of Python data structures when parsing JSON data.
Here’s an example JSON with an array of two objects representing people, each holding a “name” value and an “age” value:
[
{
"name": "John",
"age": 17
},
{
"name": "Alice",
"age": 24
}
]
JSON format is simple and easy to use, and it matches the syntax of popular languages like JavaScript and Python, making it straightforward to parse JSON strings efficiently.
For this reason, JSON is one of the most common formats for various APIs. It can also be used for data storage or configuration files.
How to Read a JSON File in Python
There are cases where you won't receive data as a JSON string but instead will have a .json file.
First, you'll need a file to use as an example. Create a new file called todos.json and paste in the first five todos from the mock API:
[
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
},
{
"userId": 1,
"id": 2,
"title": "quis ut nam facilis et officia qui",
"completed": false
},
{
"userId": 1,
"id": 3,
"title": "fugiat veniam minus",
"completed": false
},
{
"userId": 1,
"id": 4,
"title": "et porro tempora",
"completed": true
},
{
"userId": 1,
"id": 5,
"title": "laboriosam mollitia et enim quasi adipisci quia provident illum",
"completed": false
}
]
There are two steps to reading JSON data with Python: opening the file and parsing JSON data from its contents.
You can open a file with the open() function.
with open("posts.json") as f:
After that, you need to load the data from the file. Since a .json file isn’t a JSON string but structured JSON data, you need to use the json.load() function.
with open("posts.json") as f:
todos = json.load(f)
Same as loads(), this will parse the JSON data and return a Python structure that you can work with.
How to Work With a JSON String in Python
A common way you'll encounter JSON data in Python is as a JSON string: a piece of text that follows the JSON format.
For example, you might receive a JSON response from an API you call on the internet:
as r
todo r"https://jsonplaceholder.typicode.com/todos/1".text
todo
# {
# "userId": 1,
# "id": 1,
# "title": "delectus aut autem",
# "completed": false
# }
Here, todo is a Python string that follows the JSON format.
It's usually not convenient to directly work with such strings in your Python program. To access their data, you want to convert it to a Python-specific format that better reflects the data’s structure.
For example, what if instead of having the JSON object as a string, we had a Python dictionary whose keys and values matched those of the JSON object? It would certainly make it much easier to work with it.
To convert the JSON string representation into a Python dictionary, use the JSON module.
import json
The json.loads() function parses JSON data and converts JSON types into their equivalent Python data structures (for example, objects to dictionaries, arrays to lists, strings to str, and so on).
as r
import json
todo r"https://jsonplaceholder.typicode.com/todos/1").text
todo_dict = json.loads(todo
todo_dict["id"])
# 1
print(todo_dict["title"])
# delectus aut autem
print(todo_dict["completed"])
# False
Once you have parsed the JSON string, you can easily modify it like a Python object. For example, here's a function that capitalizes a todo's title.
def capitalize_title(todo):
todo['title'] = todo['title']capitalize()
return todo
You can use this function to capitalize the title of the parsed todo item.
todo_dict_capitalized = capitalize_title(todo_dict)
After making edits, you can dump the Python object back to a JSON string using json.dumps().
todo_json_updated = jsondumps(todo_dict_capitalized
print(todo_json_updated)
Once executed, this should print the following in the terminal:
{"userId": 1, "id": 1, "title": "Delectus aut autem", "completed": false}
Whitespace (spaces, new lines, etc.) doesn't matter in JSON, so it's OK if the output string has different spacing than the input. You can also add indent=4 for a more readable JSON string representation.
todo_json_updated json.dumps(todo_dict_capitalized, indent=4)
Now it should return the following:
"userId" 1,
"id": 1,
"title": "Delectus aut autem",
"completed": false
Here's the full code for this section:
as r
import json
def capitalize_title(todo):
todo['title'] = todo['title'].capitalize()
return todo
todo = r.get("https://jsonplaceholder.typicode.com/todos/1").text
todo_dict = json.loads(todo)
todo_dict_capitalized capitalize_title(todo_dict)
todo_json_updated = json.dumps(todo_dict_capitalized, indent=4)
print(todo_json_updated)
How to Use Python to Write a JSON File
As we did with the JSON string, we can capitalize and dump these todos back into a file.
Since there is now an array of objects, we need to iterate over it instead of capitalizing just one todo.
for todo in todos:
todo = capitalize_title(todo)
Same as when reading from a file, you'll need to use dump(), not dumps(), to write to a file. You'll also need to supply the file you're writing to as an argument to the function.
The code below creates a new JSON file called capitalized_todos.json and uses the JSON module to serialize and write the Python data to it.
with open("capitalized_todos.json", "w+") as f:
json.dump(todos, f, indent=4)
Here's the full code for last two sections:
import json
def capitalize_title(todo):
todo['title'] = todo['title'].capitalize()
return todo
with open("todos.json", "r") as f:
todos = json.load(f)
for todo in todos:
todo = capitalize_title(todo)
with open("capitalized_todos.json", "w+") as f:
json.dump(todos, f, indent=4)
Decoding JSON Into Custom Objects
By default, the JSON library converts JSON objects to Python dictionaries. This is good enough if you want to make quick, shallow changes while parsing JSON data for smaller Python objects.
But if you have JSON data that’s integral to your program, you'll want to represent it as a custom Python object instead.
This enables you to group data and functions that use it together, specify how the data will be printed to the console, and give access to the dot notation for invoking functions.
Here's an example Python class for todos:
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 __repr__(self):
return f"Todo {self.id} by user {self.user_id}\n{self.title}\nCompleted: {self.completed}\n"
def capitalize_title(self):
self.title = self.title.capitalize()
return self
def complete(self):
self.completed = True
return self
By using class methods, it's much easier for you to capitalize the title of any given todo and/or mark it as completed.
todo = Todo(1, 1, "delectus aut autem", True)
todo.capitalize_title().complete()
But this also creates a problem: how to convert JSON to this specific representation of todo items?
Decoding JSON into a particular object representation can be done using the object_hook parameter, which lets you define how decoded JSON objects are converted into custom Python objects during parsing.
Here's the object hook for the Todo class (add it to the class definition).
def from_dict(dct):
user_id = dct["userId"]
id = dct["id"]
title = dct["title"]
completed = dct["completed"]
return Todo(user_id, id, title, completed)
It extracts the necessary values from the dictionary and initializes an object with those values. All you need to do to use the hook is to add it as an argument to the loading function.
with open("todos.json", "r") as f
todos = json.load(f, object_hook=Todo.from_dict)
Now you can capitalize, complete, and print the todos using nice formatting.
for todo in todos:
todo.capitalize_title().complete()
print(todo)
Here's the full code for this section:
import json
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 __repr__(self):
return f"Todo {self.id by user {self.user_id}\n{self.title}\nCompleted: {self.completed}\n"
def capitalize_title(self):
self.title = self.title.capitalize()
return self
def complete(self):
self.completed = True
return self
def from_dict(dct):
user_id = dct["userId"]
id = dct["id"]
title = dct["title"]
completed dct["completed"]
return Todo(user_id, id, title, completed)
with open("todos.json", "r") as f:
todos = jsonloadf object_hookTodo.from_dict)
for todo in todos:
todo.capitalize_title().complete(
todo)
Encoding Custom Python Objects With JSONEncoder
The reverse process, dumping a Python object to JSON, is a bit harder.
Instead of making an object hook, you need to write a custom encoder class that inherits from JSONEncoder, the class responsible for encoding JSON in the JSON library. But the main idea is similar: you need to provide a method (this time called default) that shows how to convert the particular object to a dictionary.
class TodoEncoder(json.JSONEncoder):
def default(self, obj):
return {"userId": obj.user_id, "id": obj.id, "title": obj.title, "completed": obj.completed}
Now you can provide this class to the json.dump() function via the cls argument.
with open("capitalized_completed_todos.json", "w+") as f:
json.dump(todos, f, indent=4, cls=TodoEncoder)
This should create a new file called capitalized_completed_todos.json that contains the todos properly converted to JSON.
Here's the full code to decode, modify, and encode a JSON file using a custom Python class:
import json
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 __repr__(self):
return f"Todo {selfid} by user {selfuser_id}\n{self.title}\nCompleted: {self.completed}\n"
def capitalize_title(self:
self.title = self.title.capitalize()
return self
def complete(self):
self.completed = True
return self
def from_dict(dct):
user_id = dct["userId"]
id = dct["id"]
title = dct["title"]
completed = dct["completed"]
return Todo(user_id, id, title, completed)
with open("todos.json", "r") as f:
todos = json.load(f, object_hook=Todo.from_dict)
class TodoEncoder(json.JSONEncoder):
def default(self, obj):
return {"userId": obj.user_id, "id": obj.id, "title": obj.title, "completed": obj.completed}
for todo in todos:
todo.capitalize_title().complete()
print(todo)
with open("capitalized_completed_todos.json", "w+") as f:
json.dump(todos, f, indent=4, cls=TodoEncoder)
Accessing Nested JSON Keys in Python
When working with JSON data, it’s common to find nested structures: JSON objects inside other objects, or lists of objects. To access deeper data inside, you can use square brackets [ ] or the .get() method:
data = {
"user": {
"profile": {
"name": "Alice",
"age": 30
}
}
}
name = data["user"]["profile"]["name"]
age = data.get("user", {}).get("profile", {}).get("age")
print(age)
If the JSON document is deeply nested or you’re not sure about the structure, you can use recursion:
def get_nested(data, key):
if isinstance(data, dict):
if key in data:
return data[key]
for k in data:
result = get_nested(data[k], key)
if result:
return result
elif isinstance(data, list):
for item in data:
result = get_nested(item, key)
if result:
return result
return None
info = {
"company": "TechCorp",
"employees": [
{
"name": "Alice",
"department": "Engineering",
"contact": {
"email": "[email protected]",
"phone": "555-0101"
}
},
{
"name": "Bob",
"department": "Marketing",
"contact": {
"email": "[email protected]",
"phone": "555-0102"
}
}
],
"founded": 2015
}
print(get_nested(info, "email"))
print(get_nested(info, "phone"))
print(get_nested(info, "department"))
print(get_nested(info, "founded"))
print(get_nested(info, "salary")) # None is returned as there is no salary key
It helps when parsing JSON data that changes shape or has optional fields. It keeps your Python error handling clean.
How to Pretty-Print JSON in Python
Sometimes JSON strings are too messy to read, especially when dealing with long JSON arrays or deeply nested JSON objects. You can use the JSON module to pretty-print them for clarity.
import json
ugly_json = '{"userId":1,"id":1,"title":"delectus aut autem","completed":false}'
parsed = json.loads(ugly_json)
print(json.dumps(parsed, indent=4))
You can also tweak formatting:
print(json.dumps(parsed, indent=4, separators=(", ", ": "), sort_keys=True))
- indent=4 makes the JSON readable.
- separators changes comma and colon spacing.
- sort_keys=True puts keys in order.
It’s great for viewing structured JSON data or writing readable logs to a JSON file.
json.load() vs json.loads(): What’s the Difference?
| Feature | json.load() | json.loads() |
|---|---|---|
| Input | Reads from a JSON file | Reads from a JSON string |
| Use case | Used when loading JSON data from disk | Used when you parse JSON strings from an API |
| Common error | FileNotFoundError, JSONDecodeError | JSONDecodeError |
Example with json.load():
with open("data.json") as f:
data = json.load(f)
Example with json.loads():
json_str = '{"key": "value"}'
data = json.loads(json_str)
Both of these convert to a Python dictionary when parsing JSON data.
Conclusion
In this tutorial, you learned three ways of decoding and encoding JSON with Python. These should cover most situations where you need to work with JSON in your Python programs.
To try out your skills with JSON in practice, you can try doing a web scraping project and storing its results in JSON for further use.
Additionally, you can experiment with some of the free JSON APIs that can be found on the internet.