Use a JSON encoder default only for one tagged value type
Use a JSON encoder default only for one tagged value type
To use a JSON encoder default only for UUID values, test for UUID and raise TypeError for every other unsupported type. The callback is invoked only after the normal JSON encoder cannot serialize an object, so it should return a JSON-compatible replacement for the explicitly supported type and decline everything else.
The synthetic payload contains one UUID and one set. First, default=str demonstrates why a permissive callback can weaken a data contract: it turns the set into a JSON string instead of reporting that the value has no chosen JSON representation. The code decodes that result and asserts that labels is a string. It prints only that type, avoiding a nondeterministic set ordering in stdout.
The replacement callback returns a tagged object for a UUID: {"__type__":"UUID","hex":"..."}. The compact, sorted JSON output makes the tag and UUID hex value inspectable, and the assertion checks the exact encoded text. When the full payload is passed to the same callback, the UUID is handled but the set raises TypeError('unsupported type: set'). Thus the example adds support for one selected type without silently converting unrelated objects.
The json documentation specifies that default must return a JSON-encodable value or raise TypeError; uuid.UUID is documented in the uuid module. These are long-standing standard-library APIs, with no newer-version-only API used here. A tagged object still needs a separate decoding policy if round-tripping is required.
AI assistance disclosure: This article was drafted with AI assistance and verified with the synthetic fixture shown.
Example
import json
from uuid import UUID
payload = {
"id": UUID("12345678-1234-5678-1234-567812345678"),
"labels": {"internal", "urgent"},
}
permissive = json.dumps(payload, default=str, sort_keys=True)
permissive_value = json.loads(permissive)
assert isinstance(permissive_value["labels"], str)
print("permissive labels type:", type(permissive_value["labels"]).__name__)
def encode_uuid(value):
if isinstance(value, UUID):
return {"__type__": "UUID", "hex": value.hex}
raise TypeError("unsupported type: {}".format(type(value).__name__))
encoded_uuid = json.dumps(
{"id": payload["id"]},
default=encode_uuid,
sort_keys=True,
separators=(",", ":"),
)
assert encoded_uuid == (
'{"id":{"__type__":"UUID","hex":"12345678123456781234567812345678"}}'
)
print("uuid:", encoded_uuid)
try:
json.dumps(payload, default=encode_uuid, sort_keys=True)
except TypeError as error:
print("validated:", error)
Expected output:
permissive labels type: str
uuid: {"id":{"__type__":"UUID","hex":"12345678123456781234567812345678"}}
validated: unsupported type: set