Turning Python objects into bytes or text and back — JSON for APIs, CSV for tables, TOML/INI for config, pickle for Python-only caches, and struct for binary formats.
| Format | Module | Human-readable | Cross-language | Safe on untrusted input |
|---|---|---|---|---|
| JSON | json | Yes | Yes | Yes |
| CSV | csv | Yes | Yes | Yes |
| TOML | tomllib (read-only) | Yes | Yes | Yes |
| INI | configparser | Yes | Mostly | Yes |
| pickle | pickle | No | Python only | No — can run code |
| SQLite | sqlite3 | No (queryable) | Yes | Yes |
| Packed binary | struct | No | Yes (you define the layout) | Yes |
JSON basics & type mapping
import json
user = {"name": "Asha", "skills": ["python", "sql"], "active": True, "manager": None}
text = json.dumps(user) # object → str
print(text) # {"name": "Asha", "skills": ["python", "sql"], "active": true, "manager": null}
print(json.loads(text)["skills"][0]) # python
print(json.loads(json.dumps({1: (1, 2)}))) # {'1': [1, 2]} ← int keys become strings, tuples become lists| Python | JSON | Back to Python |
|---|---|---|
dict | object {} | dict (keys always str) |
list, tuple | array [] | list |
str | string | str |
int, float | number | int / float |
True / False | true / false | bool |
None | null | None |
set, datetime, Decimal, objects | TypeError | — convert first |
Formatting options
import json
data = {"name": "Zoë", "tags": ["a", "b"], "id": 7}
print(json.dumps(data)) # {"name": "Zo\u00eb", "tags": ["a", "b"], "id": 7}
print(json.dumps(data, ensure_ascii=False, sort_keys=True)) # {"id": 7, "name": "Zoë", "tags": ["a", "b"]}
print(json.dumps(data, ensure_ascii=False, separators=(",", ":"))) # {"name":"Zoë","tags":["a","b"],"id":7}import json
print(json.dumps({"id": 7, "tags": ["a", "b"]}, indent=2)){
"id": 7,
"tags": [
"a",
"b"
]
}| Option | Effect |
|---|---|
indent=2 | Pretty-print (config files, debugging) |
sort_keys=True | Stable key order — diff-friendly output, cache keys |
ensure_ascii=False | Keep non-ASCII characters instead of \uXXXX escapes |
separators=(',', ':') | Most compact output (network payloads) |
default=fn | Called for objects JSON can't encode |
allow_nan=False | Reject NaN/Infinity, which are not valid JSON |
Custom types: default and object_hook
import json
from dataclasses import dataclass, asdict, is_dataclass
from datetime import date
@dataclass
class Event:
title: str
day: date
tags: set
def encode(obj):
if isinstance(obj, date):
return obj.isoformat()
if isinstance(obj, set):
return sorted(obj)
if is_dataclass(obj):
return asdict(obj)
raise TypeError(f"{type(obj).__name__} is not JSON serializable")
text = json.dumps(Event("launch", date(2026, 9, 29), {"b", "a"}), default=encode)
print(text) # {"title": "launch", "day": "2026-09-29", "tags": ["a", "b"]}
def decode(d): # called for every JSON object, innermost first
if "day" in d:
d["day"] = date.fromisoformat(d["day"])
return d
back = json.loads(text, object_hook=decode)
print(back["day"].year, type(back["day"]).__name__) # 2026 dateimport json
try:
json.loads("{'single': 'quotes'}") # Python-style dict text is NOT JSON
except json.JSONDecodeError as e:
print(e.msg, "at position", e.pos) # Expecting property name enclosed in double quotes at position 1CSV: reading & writing
import csv, io
rows = [{"name": "Asha", "city": "Pune, MH"}, {"name": "Ravi", "city": 'Say "hi"'}]
buf = io.StringIO()
writer = csv.DictWriter(buf, fieldnames=["name", "city"])
writer.writeheader()
writer.writerows(rows)
print(buf.getvalue().splitlines()) # ['name,city', 'Asha,"Pune, MH"', 'Ravi,"Say ""hi"""'] ← commas and quotes are escaped for you
reader = csv.reader(io.StringIO(buf.getvalue()))
header = next(reader)
print(header, list(reader)) # ['name', 'city'] [['Asha', 'Pune, MH'], ['Ravi', 'Say "hi"']]
raw = "name,score\nasha,91\nravi,84\n"
scores = [int(r["score"]) for r in csv.DictReader(io.StringIO(raw))]
print(sum(scores) / len(scores)) # 87.5import csv, io
tsv = "a\tb\n1\t2\n"
print(list(csv.reader(io.StringIO(tsv), delimiter="\t"))) # [['a', 'b'], ['1', '2']]
buf = io.StringIO()
csv.writer(buf, quoting=csv.QUOTE_NONNUMERIC, lineterminator="\n").writerow(["x", 1.5, 2])
print(buf.getvalue().strip()) # "x",1.5,2pickle — Python objects as bytes
import pickle
data = {"ids": (1, 2, 3), "tags": {"py"}, "nested": [b"raw", None, 3.5]}
blob = pickle.dumps(data) # tuples, sets, bytes: all preserved
restored = pickle.loads(blob)
print(type(blob).__name__, restored == data, type(restored["ids"]).__name__) # bytes True tuplesqlite3 — a database in the standard library
import sqlite3
con = sqlite3.connect(":memory:") # or a file path
con.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
with con: # commits on success, rolls back on error
con.executemany("INSERT INTO users (name) VALUES (?)", [("Asha",), ("Ravi",)])
print(con.execute("SELECT id, name FROM users ORDER BY id").fetchall()) # [(1, 'Asha'), (2, 'Ravi')]
print(con.execute("SELECT name FROM users WHERE id = ?", (2,)).fetchone()) # ('Ravi',)
con.close()Config files: TOML & INI
import tomllib, configparser
cfg = tomllib.loads("""
[server]
host = "0.0.0.0"
port = 8080
debug = false
[database]
pool = { min = 1, max = 5 }
""")
print(cfg["server"]["port"], cfg["server"]["debug"], cfg["database"]["pool"]["max"]) # 8080 False 5
cp = configparser.ConfigParser()
cp.read_string("""
[app]
name = notes
workers = 4
debug = yes
""")
print(cp["app"]["name"], cp.getint("app", "workers"), cp.getboolean("app", "debug")) # notes 4 TrueBinary layouts with struct
import struct
# "<" little-endian, H = unsigned 16-bit, I = unsigned 32-bit, f = 32-bit float
packed = struct.pack("<HIf", 7, 65536, 1.5)
print(len(packed), packed.hex()) # 10 0700000001000000c03f
print(struct.unpack("<HIf", packed)) # (7, 65536, 1.5)
print(struct.calcsize("<HIf"), struct.calcsize("HIf")) # 10 12 ← native mode adds alignment paddingJSON vs pickle — when would you use each?
How do you serialize a datetime or a custom object to JSON?