Lesson 23: JSON & Working with Data
Serialize Python objects to JSON, load API data, and build real-world data workflows.
JSON — The Internet's Language
JSON (JavaScript Object Notation) is the universal format for APIs, configs, and data files. It looks almost exactly like Python dicts and lists — strings in double quotes, booleans lowercase (true/false), and null instead of None.
Nearly every website you use talks JSON. When a browser fetches data from a server, when a mobile app syncs, when a machine-learning model receives training config — JSON is the lingua franca. Learning it opens the door to consuming real-world data.
What You'll Learn in This Lesson
- Convert between Python and JSON with the four functions
- Pretty-print JSON for readability
- Save and load JSON files
- Understand the type mapping
The Four Functions
| Function | Direction | Example |
|---|---|---|
json.dumps(data) |
Python → JSON string | json.dumps({"a": 1}) |
json.loads(text) |
JSON string → Python | json.loads('{"a": 1}') |
json.dump(data, f) |
Python → JSON file | json.dump(d, open("f.json","w")) |
json.load(f) |
JSON file → Python | json.load(open("f.json")) |
Remember: dump goes to a string/file (serialize), load parses from a string/file (deserialize).
Memory trick: the "s" versions work on strings; the no-s versions work on file objects.
dumps/loads↔ strings;dump/load↔ files.
Type Mapping
| Python | JSON |
|---|---|
dict |
object {...} |
list |
array [...] |
str |
string |
int / float |
number |
True / False |
true / false |
None |
null |
Not serializable by default: set, tuple, custom objects, datetime. You'll get a TypeError — convert them first or pass a default= function.
The default= escape hatch:
import json
from datetime import datetime
data = {"created": datetime.now()}
def convert(obj):
if isinstance(obj, datetime):
return obj.isoformat()
raise TypeError(f"Not serializable: {obj!r}")
print(json.dumps(data, default=convert)) # {"created": "2026-08-11T..."}
Pretty Printing
json.dumps(data, indent=2) makes output readable — essential for debugging and sharing:
import json
student = {"name": "Riya", "courses": ["Python", "AI"], "active": True}
print(json.dumps(student, indent=2))
{
"name": "Riya",
"courses": [
"Python",
"AI"
],
"active": true
}
Other useful options: sort_keys=True (sorted keys), ensure_ascii=False (keep non-English characters readable).
The Real-World Flow
# 1. API returns JSON text
api_response = '{"status": "ok", "data": [1, 2, 3]}'
# 2. Parse it into Python
payload = json.loads(api_response)
print(sum(payload["data"])) # 6
# 3. Work with dicts/lists...
# 4. Save the result
with open("result.json", "w", encoding="utf-8") as f:
json.dump(payload, f, indent=2)
This parse → transform → save pipeline is the backbone of every data engineer's day.
Mental model:
dumpspacks a suitcase (Python → JSON);loadsunpacks it (JSON → Python). Draw the nesting outside-in:{= crate,[= shelf.
Fetching live JSON from an API (with requests):
import requests
import json
response = requests.get("https://api.github.com/users/amolshukla")
data = response.json() # requests parses JSON for you
print(data["public_repos"]) # work with it as plain Python
Validating with try/except
Real-world JSON is not always well-formed. Professional code wraps parsing:
import json
raw = '{"broken": ' # invalid JSON
try:
data = json.loads(raw)
except json.JSONDecodeError as e:
print("Bad JSON:", e)
This is the Lesson 21 pattern applied to data — one line of protection prevents a crash on every bad payload.
Common Mistakes to Avoid
- Mistake:
json.dumps({1, 2, 3})(a set) — Fix: TypeError; convert to a list first. - Mistake: Forgetting that JSON booleans are
true/falseandnull— Fix: Python parses them toTrue/False/Noneautomatically. - Mistake: Double-encoding — calling
json.dumpson an already-dumped string — Fix: dump once, at the end. - Mistake:
json.loadvsjson.loadsconfusion — Fix:loads= string;load= file. - Mistake: Not handling
JSONDecodeErroron external data — Fix: wraploadsin try/except.
Professional Tips & Tricks
- Use
json.dumps(..., ensure_ascii=False)to keep non-English characters readable. - Wrap
json.loadsintry/except json.JSONDecodeErrorfor robust parsing. - Flatten nested JSON with helper functions before analysis — pandas can take dicts directly.
- Use
default=to serialize dates and custom objects. - Use
indent=2andsort_keys=Truefor shareable, readable output.
Key Takeaways
- Four functions:
dumps/loads(strings) anddump/load(files). - Python and JSON map cleanly: dict↔object, list↔array, True↔true, None↔null.
indent=2pretty-prints;ensure_ascii=Falsekeeps Unicode readable.- Sets, tuples, and dates need conversion before serialization.
- Wrap parsing in try/except for robustness.
Next up: Testing & debugging — writing code that proves itself.
import json
# Python -> JSON string
student = {"name": "Riya", "courses": ["Python", "AI"], "active": True}
json_text = json.dumps(student, indent=2)
print("JSON output:")
print(json_text)
# JSON string -> Python
parsed = json.loads(json_text)
print("Parsed name:", parsed["name"])
print("First course:", parsed["courses"][0])
# Save to a file and read back
with open("student.json", "w", encoding="utf-8") as f:
json.dump(student, f, indent=2)
with open("student.json", "r", encoding="utf-8") as f:
restored = json.load(f)
print("Round-trip equal:", restored == student)Lesson Code (Python)
import json
# Python -> JSON string
student = {"name": "Riya", "courses": ["Python", "AI"], "active": True}
json_text = json.dumps(student, indent=2)
print("JSON output:")
print(json_text)
# JSON string -> Python
parsed = json.loads(json_text)
print("Parsed name:", parsed["name"])
print("First course:", parsed["courses"][0])
# Save to a file and read back
with open("student.json", "w", encoding="utf-8") as f:
json.dump(student, f, indent=2)
with open("student.json", "r", encoding="utf-8") as f:
restored = json.load(f)
print("Round-trip equal:", restored == student)Console Output
JSON output:
{
"name": "Riya",
"courses": [
"Python",
"AI"
],
"active": true
}
Parsed name: Riya
First course: Python
Round-trip equal: TrueCode Visualization Tips
- Trace dumps as 'pack a suitcase' and loads as 'unpack a suitcase'.
- Draw the nesting: { } = crate, [ ] = shelf — read from outside in.
- Print with indent=2 to literally see the structure — formatting is visualization.
Professional Tips & Tricks
- Use json.dumps(..., ensure_ascii=False) to keep non-English characters readable.
- Wrap json.loads in try/except json.JSONDecodeError for robust parsing.
- Flatten nested JSON with helper functions before analysis — pandas can take dicts directly.
Python Code Judge & Practice Arena
LeetCode StyleRun real Python 3.12 WebAssembly code directly in your browser against automated test suites.
Problem 1: JSON Category Query 1
Test Your Knowledge
Instant feedbackQuick Check: Lesson 23: JSON Serialization, APIs, Datetime & Custom Encoders
Up next · Continue learning
Testing & Debugging Like a Pro
assert, pytest, the debugger, and logging — write code that proves itself.