How do I read and write JSON in Python?
Short answer
Use json.load and json.dump for files, and json.loads and json.dumps for strings. The s versions work with strings, and common traps are non-serialisable types and forgetting that keys become strings.
The short version
Use json.load/json.dump to read and write files, and json.loads/json.dumps to work with strings. The rule for remembering which is which: the s versions deal with strings. JSON maps cleanly onto Python dicts and lists, but a few types do not serialise, and all keys come back as strings.
Strings versus files
import json
data = {"city": "Leeds", "orders": 3}
text = json.dumps(data) # dict -> JSON string
back = json.loads(text) # JSON string -> dict
The file versions read from or write to a file object directly.
with open("data.json", "w") as f:
json.dump(data, f)
with open("data.json") as f:
data = json.load(f)
Making the output readable
By default dumps produces one compact line. Pass indent for a human-friendly layout.
json.dumps(data, indent=2)
This is worth doing for files a person might read or a diff might compare, because a compact single line is hard to inspect.
The non-serialisable trap
JSON only understands a limited set of types: strings, numbers, booleans, None, lists and dicts. Common Python objects like datetime, set, and NumPy or pandas types raise TypeError.
import datetime
json.dumps({"when": datetime.date(2021, 1, 1)}) # TypeError
Convert such values to a supported type first (a date to a string with isoformat(), a set to a list), or pass a default function that tells dumps how to handle them.
json.dumps({"when": datetime.date(2021, 1, 1)}, default=str)
Keys become strings
JSON object keys are always strings, so a dict with integer keys comes back with string keys after a round trip.
json.loads(json.dumps({1: "a"})) # {'1': 'a'}, key is now a string
This surprises people who expect the integer key back. If you need integer keys, convert them yourself after loading.
Reading a JSON column into pandas
For tabular JSON, pandas.read_json or pandas.json_normalize turns records straight into a DataFrame, which is easier than looping. Use plain json for configuration and small structures, and the pandas helpers when the JSON is really a table of records.
Encoding for non-English text
If your data contains non-ASCII characters, pass ensure_ascii=False so they are written as themselves rather than as escape sequences, and open the file with encoding="utf-8". This keeps names and accented text readable in the output file.