Python DataclassのカスタムJSONエンコーダー作成
カスタムJSONEncoderサブクラスとdefault関数を使用して、Python dataclass、enum、datetime、カスタム型をJSONにシリアライズする方法を学びます。
Serialization
詳細な説明
PythonオブジェクトからJSONへのシリアライゼーション
Pythonの json.dumps() はデフォルトではdataclass、enum、datetimeオブジェクトをシリアライズできません。カスタムエンコーダーまたは default 関数が必要です。
問題点
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
解決策1:dataclasses.asdict()
from dataclasses import asdict
json.dumps(asdict(user), default=str)
asdict() はdataclassを再帰的にdictに変換します。default=str はdatetimeを str() で処理します。
解決策2:カスタムエンコーダークラス
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"}'
解決策3:default関数
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)
Pydanticの場合(組み込み)
Pydanticモデルは組み込みのシリアライゼーションを持っています:
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"}'
フィールド名の制御
dict内包表記でsnake_caseを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()}
ラウンドトリップチェックリスト
- デシリアライズ —
json.loads()+ dataclass構築またはPydanticmodel_validate_json()。 - 処理 — 型付きPythonオブジェクトで作業。
- シリアライズ — カスタムエンコーダー、
asdict()+json.dumps()、またはPydanticmodel_dump_json()。
ユースケース
FlaskやDjangoでREST APIを構築しており、dataclass、enum、datetimeフィールドを含むレスポンスオブジェクトをHTTPレスポンスボディ用のクリーンなJSONにシリアライズする必要がある場合に使用します。