JSON (JavaScript Object Notation) is the text format used by most web APIs and many configuration files. Python's built-in json module converts JSON text into dictionaries and lists, and back again, with four functions:
| Function | From | To |
|---|---|---|
json.loads(text) | JSON string | Python object |
json.dumps(obj) | Python object | JSON string |
json.load(file) | JSON file | Python object |
json.dump(obj, file) | Python object | JSON file |
The s stands for string. This lesson covers the type mapping between JSON and Python, readable output, saving and loading files, and why parsed JSON still needs validating. The examples manage a camping trip checklist.
Data mapping
JSON has six kinds of value, and each maps to a Python type:
| JSON | Python |
|---|---|
object {"a": 1} | dict |
array [1, 2] | list |
string "rope" | str |
number 3, 2.5 | int, float |
true / false | True / False |
null | None |
import json
checklist = json.loads('{"camp": "Pine Bay", "nights": 2, "booked": true, "items": ["lamp"]}')
print(type(checklist), checklist["booked"], type(checklist["nights"]))
# <class 'dict'> True <class 'int'>Going the other way, tuples are written as arrays, so they come back as lists. JSON object keys are always strings, so a dictionary with integer keys returns with string keys: {1: "a"} becomes {"1": "a"}. Sets, dates, Decimal values and your own class instances aren't supported by default, and dumps raises TypeError: Object of type set is not JSON serializable.
Strings and files
Use the s functions when the JSON is already in a string, for example an API response body, and the file functions when reading or writing a file:
import json
checklist = {"camp": "Pine Bay", "items": ["lamp", "rope"]}
with open("checklist.json", "w", encoding="utf-8") as file:
json.dump(checklist, file, indent=2)
with open("checklist.json", encoding="utf-8") as file:
loaded = json.load(file)
print(loaded == checklist) # TrueA file must be opened in text mode with an encoding for these functions. Passing a file object to loads, or a string to load, is a common mix-up that fails with TypeError or AttributeError.
Invalid JSON raises json.JSONDecodeError, which includes the line and column of the problem. Typical causes are single quotes (JSON requires double quotes), trailing commas, comments, and Python's True/None spellings instead of true/null.
Formatting
By default, dumps produces compact, single-line text. Options make it easier to read or compare:
data = {"camp": "Café du Lac", "items": ["lamp", "rope"], "nights": 2}
print(json.dumps(data, indent=2, sort_keys=True, ensure_ascii=False)){
"camp": "Café du Lac",
"items": [
"lamp",
"rope"
],
"nights": 2
}indent=2adds line breaks and indentation, ideal for files people will read and diffs in version control.sort_keys=Truewrites keys alphabetically, so the same data always produces the same text.ensure_ascii=Falsekeeps characters likeéreadable. The defaultTruewrites them as escapes such asé, which are equally valid but harder to read. When using it with files, open them withencoding="utf-8".separators=(",", ":")removes spaces for the smallest possible output.
These options change only the text. Loading any of these variants gives back the same Python data.
Validation
json.loads checks that text is syntactically valid JSON. It doesn't check that the data has the shape your program expects:
import json
def load_checklist(text):
data = json.loads(text)
if not isinstance(data, dict):
raise ValueError("checklist must be a JSON object")
items = data.get("items")
if not isinstance(items, list) or not all(isinstance(i, str) for i in items):
raise ValueError("items must be a list of strings")
return data'"rope"', '[1, 2]' and '{"items": 5}' are all valid JSON, but none is a valid checklist. Data from users, files or other services should be checked before use, especially before it reaches a database or decides an action. For larger schemas, libraries such as jsonschema or Pydantic describe the expected shape declaratively.
The json module never executes code, so parsing untrusted JSON is safe in that sense. Never use eval() to parse JSON-like text.
Independent practice and a worked check
Task: validate a campsite checklist and write readable UTF-8 JSON without serialising unsupported objects.
import json
from pathlib import Path
raw = '{"camp": "Lac d’Annecy", "items": ["tent", "stove"], "tags": ["lake"]}'
data = json.loads(raw)
if not isinstance(data.get("items"), list):
raise ValueError("items must be a list")
data["items"].append("torch")
path = Path("camp.json")
path.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
print(json.loads(path.read_text(encoding="utf-8"))["items"]) # ['tent', 'stove', 'torch']
path.unlink()Tags were stored as a list, not a set, so dumps needs no default function. For the worked check, the function that decodes JSON from a string is json.loads.
Reference and next practice
The json module documentation covers every option, and json.org defines the format. Continue with CSV handling and web APIs. Practise with the Python exercises and Python quiz.
JSON Handling Summary
- Data mapping: JSON objects map to dictionaries, arrays to lists and null to None.
- Strings and files: loads and dumps work with strings while load and dump work with file objects.
- Formatting: indent improves readability and ensure_ascii=False preserves readable Unicode characters.
- Validation: Parsing checks JSON syntax but application rules still need validation.
JSON Handling Examples
Each example shows the code, the output it produces and what each part does.
JSON Handling in a small program
import json
text = '{"camp": "Pine Bay", "items": ["lamp", "rope"]}'
checklist = json.loads(text)
checklist["items"].append("map")
print(json.dumps(checklist, sort_keys=True)){"camp": "Pine Bay", "items": ["lamp", "rope", "map"]}Line-by-line explanation
- Lines 2 to 3
loadsparses the JSON string into a dictionary containing a list. - Line 4The result is ordinary Python data, so
append()works. - Line 5
dumpsturns it back into JSON text;sort_keysmakes the key order predictable.
Saving objects JSON doesn't support
import json
from datetime import date
trip = {"camp": "Pine Bay", "start": date(2026, 7, 4), "tags": {"lake", "forest"}}
try:
json.dumps(trip)
except TypeError as error:
print("Error:", error)
def to_json(value):
if isinstance(value, date):
return value.isoformat()
if isinstance(value, set):
return sorted(value)
raise TypeError(f"Cannot serialise {type(value).__name__}")
text = json.dumps(trip, default=to_json, sort_keys=True)
print(text)
back = json.loads(text)
print(type(back["start"]).__name__, back["tags"])Error: Object of type date is not JSON serializable
{"camp": "Pine Bay", "start": "2026-07-04", "tags": ["forest", "lake"]}
str ['forest', 'lake']Line-by-line explanation
- Lines 6 to 9Without help,
dumpsfails on the first unsupported value. - Lines 11 to 16The
defaultfunction is called for each unsupported object and returns something JSON can store. - Lines 19 to 20Loading gives back a string and a list: converting them to
dateandsetagain is up to your code.
Common JSON Handling Mistakes
Mixing up load and loads
with open("checklist.json", encoding="utf-8") as f:
data = json.loads(f) # TypeError: the JSON object must be str, bytes or bytearraywith open("checklist.json", encoding="utf-8") as f:
data = json.load(f)loads takes a string; load takes a file object.
Writing Python syntax and calling it JSON
text = "{'camp': 'Pine Bay', 'booked': True}"
json.loads(text) # JSONDecodeErrortext = '{"camp": "Pine Bay", "booked": true}'
json.loads(text)JSON requires double quotes and lower-case true, false and null. Generate JSON with json.dumps() instead of building it by hand.
JSON Handling in Real Projects
JSON in real Python projects
Web APIs send and receive JSON, and libraries such as requests offer response.json() as a shortcut for json.loads(response.text). Applications store settings in JSON files, and tools such as VS Code, npm and Jupyter notebooks use it for their configuration and data. Logging systems often write one JSON object per line ("JSON Lines") so logs can be searched and analysed.
Production code typically validates incoming JSON at the boundary, converts it into dataclasses or Pydantic models, and works with typed objects from then on. That keeps JSON's loose structure from spreading through the program.
JSON Handling FAQ
What is the difference between json.load and json.loads?
json.load(file) reads JSON from an open file object. json.loads(text) parses JSON from a string. Similarly, json.dump(obj, file) writes to a file and json.dumps(obj) returns a string.
How do I pretty-print JSON in Python?
Use json.dumps(data, indent=2). Add sort_keys=True for alphabetical keys and ensure_ascii=False to keep non-English characters readable. From the command line, python -m json.tool file.json pretty-prints a file.
Why do I get "Object of type X is not JSON serializable"?
JSON supports only dictionaries, lists, strings, numbers, booleans and null. Convert other values first, for example dates to ISO strings and sets to lists, or pass a default= function to dumps that does the conversion.
How do I fix JSONDecodeError: Expecting property name enclosed in double quotes?
The text uses single quotes or has a trailing comma, which JSON doesn't allow. If the data came from Python, it was probably written with str() instead of json.dumps(). Regenerate it with json.dumps() rather than editing quotes by hand.
Are JSON and Python dictionaries the same thing?
No. JSON is a text format; a dictionary is an in-memory Python object. They look similar, but JSON requires double-quoted string keys and uses true, false and null, while dictionaries accept any hashable key and use True, False and None.
JSON Handling Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Which function decodes JSON from a string?
Which review concern is valid for json handling?
JSON Handling Quiz
2 questions to check what you learned.
Which function decodes JSON from a string?
- json.load
- json.loads
- Python ignores the distinction
Which statement correctly describes formatting?
- indent improves readability and ensure_ascii=False preserves readable Unicode characters.
- Only the example output matters, not the rule
- Every operation has the same behavior