Working with JSON and CSV
Plain text files store sentences. Real applications store structured data — and two formats run the world: JSON (the language of APIs and configs) and CSV (the language of spreadsheets). Python reads and writes both in a few lines.
JSON — dictionaries as files
JSON (JavaScript Object Notation) looks exactly like Python dicts and lists — because it was designed to be universal:
{
"name": "Aarav",
"age": 16,
"skills": ["python", "streamlit"],
"topper": true
}Python's json module converts between JSON text and Python objects:
| JSON | Python |
|---|---|
object {} | dict |
array [] | list |
| string | str |
| number | int / float |
| true / false | True / False |
| null | None |
Saving data with json.dump
import json
student = {
"name": "Aarav",
"age": 16,
"skills": ["python", "streamlit"],
}
with open("student.json", "w") as f:
json.dump(student, f, indent=2) # dump = write Python → JSON fileThe indent=2 makes the file human-readable (pretty-printed). Open student.json and you'll see neatly formatted JSON.
Loading data with json.load
import json
with open("student.json") as f:
data = json.load(f) # load = read JSON file → Python
print(data["name"]) # Aarav
print(data["skills"][0]) # python
print(type(data)) # <class 'dict'> — a real Python dict again!That's the full round trip: dict → file → dict. The loaded value is a *real* Python dictionary — index it, loop it, modify it, exactly like the ones you've built by hand.
The persistence pattern (the one from the to-do app)
import json
import os
FILE = "tasks.json"
def load_tasks():
if os.path.exists(FILE): # first run? no file yet
with open(FILE) as f:
return json.load(f)
return [] # fresh start
def save_tasks(tasks):
with open(FILE, "w") as f:
json.dump(tasks, f, indent=2)
# the app loop
tasks = load_tasks()
tasks.append("learn JSON")
save_tasks(tasks)
print(tasks)Run it twice — the task list grows. This is how apps remember between sessions, and it's the exact pattern from the to-do lesson, now explained fully.
json.dumps / json.loads — strings, not files
The s versions work with strings instead of files (d = dump string, s = load string):
text = json.dumps(student, indent=2) # dict → JSON string
print(text)
back = json.loads(text) # JSON string → dict
print(back["name"])dumps is how you'd send JSON over a network or embed it in an API request.
CSV — the spreadsheet format
CSV (Comma-Separated Values) is what Excel, Google Sheets, and every data export speak:
name,age,grade
Aarav,16,A
Diya,15,A
Kabir,16,BPython's csv module handles the parsing (including the messy edge cases like commas *inside* values):
import csv
# Write
with open("students.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["name", "age", "grade"]) # header
writer.writerow(["Aarav", 16, "A"])
writer.writerow(["Diya", 15, "A"])
# Read
with open("students.csv") as f:
reader = csv.reader(f)
for row in reader:
print(row) # each row is a list: ['Aarav', '16', 'A']The newline="" in the write open is a required quirk on some systems — include it by habit. Each row arrives as a list of strings — numbers included, so convert as needed.
DictReader — CSV with named columns
import csv
with open("students.csv") as f:
reader = csv.DictReader(f) # uses the header row as keys
for row in reader:
print(row["name"], "— grade", row["grade"])DictReader turns each row into a dictionary keyed by the header — no more remembering that column 1 is age. For anything beyond tiny files, prefer it.
Common Errors & Fixes
.get() it or fix the source.f.write instead of csv.writer; let the module handle commas and quotes.newline="" in the open.✅ Checkpoint
json.dumps for? *(Dict → JSON *string*, no file involved)*os.path.exists before load? *(First run has no file — return a fresh default)*csv.DictReader give you per row? *(A dict keyed by the header names)*Module 8 checkpoint reached — errors handled, files read and written, data persisting.
Next module: Object-Oriented Python — the mindset behind every big codebase.