The Language APIs Speak
APIs exchange data as JSON — text like {"id": 1, "name": "Ana"}. Python's json module translates in both directions: json.dumps turns Python data into JSON text, json.loads parses JSON text into Python data.
There is no mapping layer to configure: the keys are the field names, as written. A dataclass gives a record a fixed shape, and dataclasses.asdict turns it into a plain dict ready for json.dumps — rename keys by hand when the API needs a different spelling.
From Python Data to JSON
json.dumps produces one-line JSON for machines; json.dumps(..., indent=2) produces readable JSON for humans and logs. Nothing is dropped automatically — when an empty field should stay out of the JSON, delete it from the dict before dumping.
One line and pretty
Ana has no email, and the helper removes the empty field from her dict — her JSON stays clean. Ben's full record prints indented.
Output: complete Python program
import json
from dataclasses import dataclass, asdict
@dataclass
class User:
id: int
name: str
email: str = ""
def to_json_dict(user):
data = asdict(user) # keys are the field names, as written
if data["email"] == "":
del data["email"] # drop the empty field by hand
return data
ana = User(id=1, name="Ana")
print(json.dumps(to_json_dict(ana)))
ben = User(id=2, name="Ben", email="ben@example.com")
print(json.dumps(to_json_dict(ben), indent=2))
From JSON to Python Data
json.loads parses JSON into plain Python data — dicts, lists, strings, numbers. Unknown keys are not thrown away: everything in the JSON ends up in the dict, and a typed record is built from just the fields it needs. Bad JSON raises json.JSONDecodeError — catch it; it is the signal that the text was not JSON at all.
Loads and the extra key
The JSON carries an extra key that the User dataclass does not have — json.loads keeps it in the dict, and the dataclass simply picks the two fields it wants. Broken JSON, on the other hand, raises a real error.
Output: complete Python program
import json
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
data = '{"id": 7, "name": "Cleo", "extra": "kept"}'
raw = json.loads(data)
print(raw) # the unknown key is kept in the dict
user = User(id=raw["id"], name=raw["name"]) # pick the fields you need
print(user)
try:
json.loads('{"id": ')
except json.JSONDecodeError as err:
print("error:", err)
Nested Data
Real API payloads nest: an order carries its user and a list of items. Dataclasses inside dataclasses and lists map to JSON objects and arrays automatically — asdict walks the whole structure.
An order with a user inside
The nested User dataclass becomes a nested JSON object, the items list becomes a JSON array — the shape of the Python data is the shape of the JSON.
Output: complete Python program
import json
from dataclasses import dataclass, asdict
@dataclass
class User:
id: int
name: str
@dataclass
class Order:
id: int
items: list[str]
user: User
order = Order(id=1042, items=["keyboard", "mouse"], user=User(id=1, name="Ana"))
print(json.dumps(asdict(order), indent=2))