Practical guide · verified against the real thing
How to parse JSON in Python (and fix the errors everyone hits)
In one line: json.loads vs json.load, safe access to nested data, the datetime gotcha, and how to read a JSONDecodeError by line and column.
Python ships the whole JSON toolchain in its standard library — no installation, one import. The entire skill is four functions and a handful of habits, so here is the practical version: parsing, safe access, the error that starts every debugging session, and the type error that ambushes everyone once.
Parsing: loads vs load
The one-letter distinction confuses people forever: loads parses a string, load (no s) reads from an open file. In practice:
import json
raw = '{"city": "Lagos", "temp_c": 27.5, "days": ["Mon", "Tue"]}'
data = json.loads(raw)
print(data["city"])
print(data["days"][0])
print(data.get("humidity", "not reported"))
JSON objects become Python dictionaries, arrays become lists, and the value types map cleanly (string, number, boolean, null → None). The .get() line is the habit worth stealing: missing keys raise KeyError with square brackets, and API responses omit fields all the time — .get("key", default) turns that into a non-event.
Reading from a file is the same idea with load:
import json
with open("weather.json", encoding="utf-8") as f:
data = json.load(f)
clean = json.dumps(data, indent=2, sort_keys=True)
print(clean[:200])
dumps is the reverse (Python → JSON string), and indent=2 is what makes it readable — the same transformation the JSON formatter tool does in your browser.
Reading the error instead of fearing it
Invalid JSON raises json.JSONDecodeError, and it is a genuinely helpful exception — it tells you exactly where the parse died:
import json
broken = '{"city": "Lagos", "temp_c": }'
try:
json.loads(broken)
except json.JSONDecodeError as e:
print(f"Line {e.lineno}, column {e.colno}: {e.msg}")
Ninety percent of real-world JSON errors are one of the format's three strictness rules — single quotes instead of double, a trailing comma, or a comment where comments aren't allowed (the reasons live in what JSON actually is). Run the parse, read the line and column, look there first.
The one ambush: non-JSON types
dumps happily serialises dicts, lists, strings, numbers, booleans and None — and raises TypeError on everything else, most often datetime objects. The standard fix is to convert to a string yourself before serialising (dt.isoformat() is the conventional choice) rather than teaching the serialiser tricks. If a script reads timestamps from an API, they arrive as strings; converting them with datetime.fromisoformat() beats arithmetic on text every time. What to do with parsed data next — storing it properly — is the follow-up: SQLite, in a few honest lines.
Sources
Next