A reusable encoder class plugs into frameworks that accept cls=, and streaming avoids building huge strings.
json.JSONEncoder is the class behind dumps. Subclass it and override default(self, o) to get the same behaviour as the default= function, but bundled with its options and reusable as cls=:
json.dumps(data, cls=AppEncoder)Many libraries (Django's JsonResponse, some HTTP clients) take an encoder class rather than a function, which is the main reason to prefer a subclass. In default, call super().default(o) at the end; it raises the standard TypeError.
encoder.encode(obj) returns the whole string, while encoder.iterencode(obj) yields it in chunks. Writing those chunks to a file or socket avoids holding the entire document in memory as one string. json.dump already uses iterencode internally when writing to a file.
Options such as indent, sort_keys, ensure_ascii and check_circular are constructor arguments of the encoder.
You cannot change how built-in types (int, float, str, dict) are formatted by overriding default, and overriding encode isn't enough because the C accelerator handles nested values directly. If you need special float formatting, convert values beforehand (for example round them), or use a third-party library.
Similarly, json.JSONDecoder can be subclassed and passed as cls= to loads, mostly to bundle hooks.
Example
import io
import json
from datetime import date
from enum import Enum
class Color(Enum):
RED = "red"
class AppEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, date):
return o.isoformat()
if isinstance(o, Enum):
return o.value
return super().default(o) # raises TypeError
data = {"day": date(2025, 1, 31), "color": Color.RED}
print(json.dumps(data, cls=AppEncoder)) # {"day": "2025-01-31", "color": "red"}
out = io.StringIO()
enc = AppEncoder(separators=(",", ":"))
rows = [{"n": i} for i in range(3)]
for chunk in enc.iterencode(rows): # streamed in pieces
out.write(chunk)
print(out.getvalue()) # [{"n":0},{"n":1},{"n":2}]Remember
- Subclass
JSONEncoderand overridedefault; pass it ascls= - Finish
defaultwithsuper().default(o)to raise the standard error iterencodeyields chunks so huge documents needn't be one string
Watch out for
- Trying to change float formatting by overriding
default; it is never called for floats.