Skip to main content

Lib/json/ (part 2)

Source:

cpython 3.14 @ ab2d84fe1023/Lib/json/encoder.py

This annotation covers the JSON encoder. See modules_json_detail2 for json.loads, JSONDecoder, and the C accelerator _json.scanstring.

Map

LinesSymbolRole
1-80JSONEncoder.__init__Configure sort_keys, indent, separators, default
81-200JSONEncoder.defaultOverride point for custom serialization
201-360JSONEncoder.encodeSerialize to a single string
361-550JSONEncoder.iterencodeLazy iterator yielding string chunks
551-700_make_iterencodeRecursive closure implementing the encoding state machine

Reading

JSONEncoder.encode

# CPython: Lib/json/encoder.py:200 encode
def encode(self, o):
"""Return a JSON string representation of a Python data structure."""
# Fast path for simple scalars
if isinstance(o, str):
if self.ensure_ascii:
return encode_basestring_ascii(o)
else:
return encode_basestring(o)
# Otherwise use the full iterencode machinery
chunks = self.iterencode(o, _one_shot=True)
if not isinstance(chunks, (list, tuple)):
chunks = list(chunks)
return ''.join(chunks)

encode returns the complete JSON string at once. iterencode yields chunks lazily and is used for streaming large objects.

_make_iterencode

# CPython: Lib/json/encoder.py:420 _make_iterencode
def _make_iterencode(markers, _default, _encoder, _indent, _key_separator,
_item_separator, _sort_keys, _skipkeys, _one_shot,
ValueError=ValueError, ...):

def _iterencode(o, _current_indent_level):
if isinstance(o, str):
yield _encoder(o)
elif o is None:
yield 'null'
elif o is True:
yield 'true'
elif o is False:
yield 'false'
elif isinstance(o, int):
yield _intstr(o)
elif isinstance(o, float):
yield _floatstr(o)
elif isinstance(o, (list, tuple)):
yield from _iterencode_list(o, _current_indent_level)
elif isinstance(o, dict):
yield from _iterencode_dict(o, _current_indent_level)
else:
# Fall back to default()
o = _default(o)
yield from _iterencode(o, _current_indent_level)

return _iterencode

The circular reference detector uses markers: a dict mapping id(obj) to the object. If the same id appears during recursion, it raises ValueError("Circular reference detected").

Float serialization

# CPython: Lib/json/encoder.py:280 FLOAT_REPR
# NaN and Infinity are not valid JSON
def _floatstr(o):
if math.isnan(o) or math.isinf(o):
raise ValueError("Out of range float values are not JSON serializable: " + repr(o))
return repr(o)

By default json.dumps(float('nan')) raises ValueError. Setting allow_nan=True (non-standard) outputs bare NaN/Infinity.

JSONEncoder.default

# CPython: Lib/json/encoder.py:180 default
def default(self, o):
"""Override this method to serialize custom Python objects."""
raise TypeError(f'Object of type {type(o).__name__} is not JSON serializable')

Subclass and override default to handle types like datetime, Decimal, or dataclasses:

class MyEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, datetime):
return o.isoformat()
return super().default(o)

gopy notes

json.JSONEncoder is module/json.JSONEncoder in module/json/encoder.go. The C accelerator _json.encode_basestring_ascii is module/json.EncodeBaseStringASCII. _make_iterencode is implemented as a Go generator using a chan string or a recursive Go function yielding into a strings.Builder.