Skip to content
intermediate Phase 15 · Python Standard Library Deep Dive

JSON & Data Serialization

Serialize and deserialize JSON, YAML, and binary data formats.

45m
0 problems
Topic Progress 0%

JSON Round-Trips with the json Module

JSON Round-Trips with the json Module

import json
from dataclasses import dataclass, asdict

@dataclass
class Order:
    id: str
    qty: int

order = Order("A-1", 3)
text = json.dumps(asdict(order), indent=2, sort_keys=True, ensure_ascii=False)
same = json.loads(text, object_hook=lambda d: Order(**d))   # or explicit parsing

Options that matter in production:

  • ensure_ascii=False keeps UTF-8 readable (default escapes everything)
  • indent=2 for humans; omit for compact wire payloads
  • default=... handles objects json does not know (datetime → isoformat())
  • json.loads(s, parse_float=Decimal) preserves precision for money
  • Keys must be strings (int keys coerce; tuples and objects raise TypeError)

Dataclasses as the Serialization Layer

Dataclasses as the Serialization Layer

from dataclasses import dataclass, field, asdict, astuple, replace

@dataclass
class Line:
    sku: str
    tags: list[str] = field(default_factory=list)   # never default=[]

d = asdict(line)        # deep dict — safe for nested dataclasses
t = astuple(line)       # tuple form when position matters
new = replace(line, sku="B-2")   # copy with one field changed (works on frozen too)

Why default_factory: a shared tags=[] default would be one list object reused by every instance — mutations leak across instances and across tests. asdict recurses through nested dataclasses, so one call produces the whole tree ready for json.dumps; non-dataclass values inside (datetime, Decimal) still need a custom default encoder.

YAML, Pickle, and Binary Formats

YAML, Pickle, and Binary Formats

YAML (PyYAML): yaml.safe_load accepts only plain data — lists, dicts, scalars. Never yaml.load without a restricted SafeLoader: full load can construct arbitrary Python objects and execute code from a document.

Pickle: fastest way to round-trip Python objects, but never on untrusted bytes — a payload can run code on unpickle. It is also version-fragile across Python releases and useless to non-Python clients.

Binary options: MessagePack (JSON-shaped, compact, schema-less), Protobuf/Avro (schema + codegen, evolution-friendly) — choose these when payload size or cross-language contracts dominate.

Strict JSON gotchas: NaN/Infinity are accepted by Python by default (allow_nan=False rejects them), and JSON has no datetime type — timestamps must be strings or epoch numbers.

Quiz

1. Why use field(default_factory=list) instead of tags=[] on a dataclass?

Question 1 options

2. What is the danger of yaml.load(user_input)?

Question 2 options

3. Why never unpickle untrusted data?

Question 3 options

Flashcards

Question

json.dumps default=

Answer

Hook converting unknown objects (datetime, Decimal) into serializable values.

Question

asdict vs astuple

Answer

asdict → nested dict for JSON; astuple → positional tuple; both recurse dataclasses.

Question

yaml.safe_load

Answer

Parses plain data only; the safe alternative to the code-executing default load.

Question

Pickle trust rule

Answer

Deserializing pickle = executing code; only use on data you produced.

Revision Notes

Key Takeaways

  • 1. json + dataclasses.asdict covers most serialization with explicit hooks for edge types
  • 2. Mutable dataclass defaults require default_factory to avoid shared state
  • 3. Match the format to trust and audience: safe YAML for config, pickle only for self-produced caches

Interview Tips

  • • Name the shared-mutable-default bug and show the one-line factory fix
  • • Explain why JSON beats pickle for cross-service interchange, including the security argument

Cheat Sheet

json.dumps(obj, indent=2, ensure_ascii=False, default=f); asdict()/replace(); yaml.safe_load; pickle only trusted; allow_nan=False for strict JSON.

Continue Learning