Короткий ответ
Модуль json сериализует только базовые типы (dict, list, str, int, float, bool, None); для datetime, Decimal, UUID и подобных нужно либо привести их к строке/числу вручную, либо передать кастомный default-обработчик в json.dumps, либо использовать библиотеку с готовой поддержкой (например, Pydantic в FastAPI).
Как это работает подробнее
json.dumps рекурсивно обходит структуру и для каждого значения ищет соответствие в таблице типов; при встрече неизвестного типа вызывается функция default, если она передана, иначе выбрасывается TypeError.
Пример кода
example.py
import json
from datetime import datetime, timezone
def default_serializer(obj):
if isinstance(obj, datetime):
return obj.astimezone(timezone.utc).isoformat()
raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
payload = {"created_at": datetime.now(timezone.utc), "name": "order-1"}
result = json.dumps(payload, default=default_serializer)
print(result) # {"created_at": "2024-05-01T12:00:00+00:00", "name": "order-1"}Что использовать на практике
- Рекомендация. в большинстве backend-фреймворков эта проблема решена за вас (FastAPI/Pydantic сами знают, как сериализовать datetime в ISO 8601), но при ручной работе с
jsonили при сериализации нестандартных объектов нужно явно описывать преобразование.
Подводные камни
- Риски. пытаться сериализовать
datetimeнапрямую без обработчика и получать необработанное исключение в проде; сериализовывать datetime без таймзоны, из-за чего клиент не может корректно интерпретировать время; вручную конвертировать Decimal в float, теряя точность для денежных величин.