Batu Lab NotesPractical developer guides

Test a JSON encoder with a nested Decimal rejection

By Batu · English technical notes

A JSON encoder test should place Decimal at the nested boundary you need to protect, then assert that default raises TypeError. A top-level-only precheck can miss {'outer': {'amount': Decimal('1.20')}} entirely.

The deliberately weak top_level_decimal_check() iterates only over the outer dictionary’s values. Its sole value is another dictionary, so it returns successfully and the script prints that the weak check passed. This is a demonstration of the check’s scope, not evidence that the data is JSON encodable.

json.dumps() then walks into the nested mapping. When it reaches Decimal('1.20'), it calls the supplied reject_decimal function. That function raises a clear TypeError, establishing the policy at the actual encoder extension point. The assertion checks the precise message, which makes accidental conversion to a float or string visible in a regression test. It does not make a general statement about all custom encoders; another default function could intentionally choose a representation for Decimal.

The json documentation describes default as the hook for objects the encoder cannot serialize, and the decimal documentation defines Decimal as a distinct decimal-arithmetic type. These APIs are long established; Python 3.8+ is a practical minimum.

AI assistance disclosure: this article was drafted with AI assistance and uses a synthetic in-memory fixture.

import json
from decimal import Decimal


def top_level_decimal_check(mapping):
    for value in mapping.values():
        if isinstance(value, Decimal):
            raise TypeError("top-level Decimal")


def reject_decimal(value):
    if isinstance(value, Decimal):
        raise TypeError(f"Decimal is not JSON data: {value}")
    raise TypeError(f"unsupported type: {type(value).__name__}")


payload = {"outer": {"amount": Decimal("1.20")}}
top_level_decimal_check(payload)
print("weak check passed")

try:
    json.dumps(payload, default=reject_decimal)
except TypeError as error:
    assert str(error) == "Decimal is not JSON data: 1.20"
    print(error)
weak check passed
Decimal is not JSON data: 1.20