Parse, Create & Manage JSON Data | Python for Beginners #18
Video: Parse, Create & Manage JSON Data | Python for Beginners #18 by Taught by Celeste AI - AI Coding Coach
Python JSON: dumps, loads, dump, load
json.dumps(obj)→ string.json.loads(s)→ Python object.json.dump(obj, f)→ write to file.json.load(f)→ read from file. Type mapping: dict ↔ object, list ↔ array, True/False/None ↔ true/false/null.
JSON is the lingua franca of web APIs and config files. Python's json module makes round-tripping trivial.
What JSON looks like
{
"name": "Alice",
"age": 25,
"active": true,
"hobbies": ["reading", "coding"],
"address": null
}
Key/value pairs in {}, lists in [], strings in "", numbers, true/false/null. That's all of JSON.
Python ↔ JSON type mapping
| Python | JSON |
|---|---|
dict |
object {} |
list, tuple |
array [] |
str |
string |
int, float |
number |
True |
true |
False |
false |
None |
null |
Note tuples become arrays — and come back as lists. JSON has no tuple, set, or datetime types; you have to convert manually.
Python → JSON string: dumps
import json
user = {
"name": "Alice",
"age": 25,
"hobbies": ["reading", "coding"],
"active": True,
"score": None
}
s = json.dumps(user)
print(s)
# {"name": "Alice", "age": 25, "hobbies": ["reading", "coding"], "active": true, "score": null}
json.dumps(obj) returns a JSON string. The s is for "string."
Note that True becomes true, None becomes null — Python casing → JSON casing automatically.
Pretty printing
pretty = json.dumps(user, indent=2)
print(pretty)
Output:
{
"name": "Alice",
"age": 25,
"hobbies": [
"reading",
"coding"
],
"active": true,
"score": null
}
indent=2 (or 4) adds line breaks and indentation. Useful for human-readable output.
Other options:
sort_keys=True— alphabetize keys.separators=(",", ":")— minimal whitespace (smaller output).ensure_ascii=False— keep Unicode characters as-is (default escapes them).
JSON string → Python: loads
text = '{"name": "Bob", "age": 30, "active": false}'
parsed = json.loads(text)
print(parsed["name"]) # Bob
print(parsed["age"]) # 30
print(parsed["active"]) # False (Python bool)
json.loads(s) parses a JSON string and returns Python objects.
If the string is malformed:
try:
json.loads("{not valid json}")
except json.JSONDecodeError as e:
print(f"Bad JSON: {e}")
JSONDecodeError includes msg, doc, pos — useful for debugging.
File I/O: dump and load
students = [
{"name": "Alice", "grade": "A", "score": 95},
{"name": "Bob", "grade": "B", "score": 82},
]
# Write
with open("students.json", "w") as f:
json.dump(students, f, indent=2)
# Read
with open("students.json", "r") as f:
loaded = json.load(f)
print(f"Loaded {len(loaded)} students")
Note: dumps/loads for strings, dump/load for files. The trailing s is the only difference.
For UTF-8 files (which all JSON should be):
with open("data.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
Nested structures
order = {
"id": "ORD-001",
"customer": {
"name": "Alice",
"email": "alice@example.com"
},
"items": [
{"product": "Widget", "qty": 3, "price": 9.99},
{"product": "Gadget", "qty": 1, "price": 24.99}
],
"total": 54.96
}
s = json.dumps(order, indent=2)
restored = json.loads(s)
print(restored["customer"]["name"]) # Alice
print(restored["items"][0]["product"]) # Widget
Nested dicts and lists work transparently. Access nested data with chained [...].
Encoding non-JSON types
from datetime import datetime
data = {"timestamp": datetime.now()}
json.dumps(data)
# TypeError: Object of type datetime is not JSON serializable
Datetimes, sets, custom objects don't map to JSON. Three options:
1. Pre-convert manually:
data = {"timestamp": datetime.now().isoformat()}
json.dumps(data)
2. Custom encoder:
class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)
json.dumps({"ts": datetime.now()}, cls=DateTimeEncoder)
3. default= callback:
def convert(obj):
if isinstance(obj, datetime):
return obj.isoformat()
raise TypeError(f"Cannot encode {type(obj)}")
json.dumps({"ts": datetime.now()}, default=convert)
Decoding into custom types
from datetime import datetime
def parse_dates(d):
for k, v in d.items():
if isinstance(v, str) and v.endswith("Z"):
try:
d[k] = datetime.fromisoformat(v.rstrip("Z"))
except ValueError:
pass
return d
raw = '{"name": "event", "when": "2026-01-15T10:00:00Z"}'
data = json.loads(raw, object_hook=parse_dates)
print(type(data["when"])) # datetime
object_hook receives each parsed dict; return the version you want stored.
For complex schemas, use pydantic or dataclasses-json — proper schema validation.
Compact and minified
data = {"a": 1, "b": [2, 3]}
# Default (with spaces)
json.dumps(data)
# '{"a": 1, "b": [2, 3]}'
# Compact
json.dumps(data, separators=(",", ":"))
# '{"a":1,"b":[2,3]}'
Compact output saves bytes for network transmission.
JSON Lines (NDJSON)
# One JSON object per line — common for logs and streaming
records = [{"id": 1}, {"id": 2}, {"id": 3}]
with open("data.jsonl", "w") as f:
for r in records:
f.write(json.dumps(r) + "\n")
# Read line by line
with open("data.jsonl", "r") as f:
for line in f:
record = json.loads(line)
print(record)
Easy to append, easy to stream. Don't try to parse this with json.load — that expects one document.
A real-world flow: load, modify, save
# Read existing
with open("students.json") as f:
students = json.load(f)
# Modify
students.append({"name": "Diana", "grade": "A", "score": 96})
for s in students:
s["honors"] = s["score"] >= 90
# Write back
with open("students.json", "w") as f:
json.dump(students, f, indent=2)
Read into Python objects, work in Python, write back. JSON is the boundary; in between, you have full Python.
Common stumbles
Single quotes. JSON requires double quotes. '{"name": "Alice"}' is fine in Python but the inner JSON must use ".
Trailing commas. JSON doesn't allow [1, 2, 3,] — Python does. Be careful when hand-writing JSON.
True vs true. Inside JSON, lowercase. The library handles this automatically when serializing/parsing.
Unicode escaping. By default, json.dumps escapes non-ASCII ("héllo" → "héllo"). Pass ensure_ascii=False to keep it as-is.
json.load on a string. loads for strings, load for file objects. Easy to mix up.
Loading huge files all at once. Each json.load parses the whole file. For multi-GB files, use a streaming parser (ijson package) or JSON Lines.
Treating tuples as JSON arrays. Tuples serialize as arrays — but come back as lists. If you need tuples, convert after parsing.
What's next
Lesson 19: classes and objects. class, __init__, self, attributes, methods.
Recap
json.dumps(obj) / loads(s) for strings; json.dump(obj, f) / load(f) for files. Type mapping: dict↔object, list↔array, True/False/None↔true/false/null. Use indent= for pretty output, ensure_ascii=False for Unicode-as-is. For non-JSON types (datetimes, sets), supply a custom encoder or pre-convert. Use JSON Lines for streamable record-per-line storage.
Next lesson: classes and objects.