Create Custom JSON Encoders for Python Dataclasses
Learn how to serialize Python dataclasses, enums, datetimes, and custom types back to JSON using custom JSONEncoder subclasses and default functions.
Detailed Explanation
Serializing Python Objects Back to JSON
Python's json.dumps() cannot serialize dataclasses, enums, or datetime objects by default. You need a custom encoder or a default function.
The Problem
import json
from dataclasses import dataclass
from datetime import datetime
@dataclass
class User:
id: int
name: str
created_at: datetime
user = User(id=1, name="Alice", created_at=datetime.now())
json.dumps(user) # TypeError: Object of type User is not JSON serializable
Solution 1: dataclasses.asdict()
from dataclasses import asdict
json.dumps(asdict(user), default=str)
asdict() converts the dataclass to a dict recursively. The default=str handles datetime by calling str() on it.
Solution 2: Custom Encoder Class
from dataclasses import dataclass, asdict
from datetime import datetime, date
from enum import Enum
import json
class CustomEncoder(json.JSONEncoder):
def default(self, obj):
if hasattr(obj, "__dataclass_fields__"):
return asdict(obj)
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, date):
return obj.isoformat()
if isinstance(obj, Enum):
return obj.value
if isinstance(obj, set):
return list(obj)
return super().default(obj)
json.dumps(user, cls=CustomEncoder)
# '{"id": 1, "name": "Alice", "created_at": "2024-06-15T09:30:00"}'
Solution 3: Default Function
def json_default(obj):
if hasattr(obj, "__dataclass_fields__"):
return asdict(obj)
if isinstance(obj, (datetime, date)):
return obj.isoformat()
if isinstance(obj, Enum):
return obj.value
raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
json.dumps(user, default=json_default)
With Pydantic (Built-in)
Pydantic models have built-in serialization:
class User(BaseModel):
id: int
name: str
created_at: datetime
user.model_dump_json()
# '{"id":1,"name":"Alice","created_at":"2024-06-15T09:30:00"}'
Controlling Field Names
Use json.dumps() with a dict comprehension to convert snake_case to camelCase:
def to_camel(s: str) -> str:
parts = s.split("_")
return parts[0] + "".join(p.capitalize() for p in parts[1:])
data = {to_camel(k): v for k, v in asdict(user).items()}
Round-Trip Checklist
- Deserialize —
json.loads()+ dataclass construction or Pydanticmodel_validate_json(). - Process — Work with typed Python objects.
- Serialize — Custom encoder,
asdict()+json.dumps(), or Pydanticmodel_dump_json().
Use Case
You are building a REST API with Flask or Django where you need to serialize response objects containing dataclasses, enums, and datetime fields into clean JSON for the HTTP response body.
Try It — JSON to Python Converter
Related Topics
Handle JSON Date Strings in Python with datetime
Advanced Patterns
Convert JSON String Values to Python Enums
Advanced Patterns
Convert Simple JSON to a Python Dataclass
Dataclasses
Convert JSON to a Pydantic BaseModel in Python
Pydantic & TypedDict
Convert REST API Response JSON to Python Classes
Advanced Patterns