Working with JSON
Parse and serialise JSON — the wire format of the web — with Python's json module.
- Parse a JSON string or file with json.loads() and json.load()
- Serialise Python objects to JSON with json.dumps() and json.dump()
- Use indent and sort_keys to produce readable output
- Handle JSONDecodeError for invalid input
- Explain the Python-to-JSON type mapping and the datetime caveat
JSON (JavaScript Object Notation) is the dominant format for data exchange on the
web — REST APIs return it, configuration files use it, services pass it in
messages. Python's standard-library json module converts between JSON text and
Python objects in both directions.
Parsing JSON
Two functions read JSON:
json.loads(s)— parses a string (thesis for string).json.load(f)— reads from a file object.
import json
text = '{"name": "Alice", "scores": [91, 85, 78]}'
data = json.loads(text)
print(data["name"]) # "Alice"
print(data["scores"][0]) # 91The result is a plain Python dict, list, string, int, float, bool, or None —
you work with it just like any other Python object.
# Reading from a file
with open("config.json") as f:
config = json.load(f)Serialising to JSON
Two matching functions write JSON:
json.dumps(obj)— returns a JSON string.json.dump(obj, f)— writes to a file object.
user = {"name": "Bob", "age": 25, "active": True}
print(json.dumps(user))
# '{"name": "Bob", "age": 25, "active": true}'Notice that Python True becomes JSON true — the module handles capitalisation
automatically.
Readable output with indent and sort_keys
By default json.dumps produces a compact single line. For human-readable output
(config files, debugging), pass indent:
print(json.dumps(user, indent=2, sort_keys=True))sort_keys=True alphabetises the keys, which makes diffs cleaner when the data
is version-controlled.
Round-tripping and nested structures
JSON handles arbitrary nesting — dicts of lists of dicts — and Python mirrors that directly. A common pattern is reading, transforming, and writing back:
with open("records.json") as f:
records = json.load(f)
for r in records:
r["score"] = r["score"] * 1.1 # bump every score by 10 %
with open("records.json", "w") as f:
json.dump(records, f, indent=2)Handling errors
If the JSON text is malformed, json.loads raises json.JSONDecodeError
(a subclass of ValueError). Always wrap untrusted input:
import json
raw = '{"name": "Alice", broken}' # invalid JSON
try:
data = json.loads(raw)
except json.JSONDecodeError as e:
print(f"Bad JSON at line {e.lineno}: {e.msg}")The datetime caveat
Python's datetime is not a JSON-native type — trying to serialise one raises a
TypeError. The standard workaround is to convert to an ISO string before
dumping:
from datetime import datetime
import json
event = {"at": datetime.now().isoformat(), "type": "login"}
print(json.dumps(event)) # works — isoformat() returns a plain stringThe JSON type mapping: dict → object, list/tuple → array, str → string, int/float → number, True/False → true/false, None → null. Anything else (datetime, custom objects, sets) needs a manual conversion before serialising.
Where to go next
Next: Data transformation patterns — sorting, grouping, counting, and reshaping data with Python's built-in tools.