問題の概要Pythonの辞書をJSON文字列に変換しようとした際、プログラムがクラッシュしてしまうのは、開発者にとってよくある経験です。標準の json.dumps() メソッドを使用しても、正常な文字列の代わりに次のようなスタックトレースが表示されます:
TypeError: Object of type datetime is not JSON serializable
このエラーは通常、APIにデータを送信したり、レコードをディスクに保存したりするタイミングで発生します。特にPostgreSQL、MySQL、MongoDBなどからデータを取得し、タイムスタンプ列が自動的にPythonの datetime オブジェクトに変換される場合に、この問題に直面することが多くなります。
エラーを引き起こすコード例```
import json from datetime import datetime
data = { "id": 1024, "event": "User Signup", "timestamp": datetime.now() # このオブジェクトがクラッシュの原因になります }
この行で TypeError が発生します
json_data = json.dumps(data)
## 発生原因Pythonに組み込まれている `json` モジュールは効率的ですが、制約があります。デフォルトでは、文字列、整数、浮動小数点、ブール値など、JSONに直接対応する基本型のみをマッピングできます。JSON自体にはネイティブな「日付(Date)」型が存在しません。仕様として日付を文字列にするべきか数値にするべきかが定義されていないため、Pythonは独自に推測して処理することを拒否し、単に処理を停止します。
## 解決方法### 方法 1:手軽な修正('default' 引数の使用)小規模なスクリプトの場合は、`json.dumps()` の `default` パラメータに関数を渡します。これにより、認識できないオブジェクトに遭遇した際にエンコーダーがどのように対処すべきかを指示できます。
import json from datetime import datetime
data = {"timestamp": datetime.now()}
シリアル化できないオブジェクトを強制的に文字列に変換します
json_data = json.dumps(data, default=str)
print(json_data)
出力: {"timestamp": "2024-06-17 14:30:05.123456"}
`default=str` は高速ですが、少し強引な手法です。ISO 8601 のような、より標準化された形式を好む場合もあるでしょう。
### 方法 2:カスタムJSONエンコーダー(本番環境に推奨)大規模なアプリケーションを構築している場合は、再利用可能なエンコーダークラスを使用するのが最善です。これにより、シリアル化のロジックを1か所にまとめ、API全体での一貫性を確保できます。
import json from datetime import datetime, date
class DateTimeEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, (datetime, date)): return obj.isoformat() return super().default(obj)
data = { "created_at": datetime.now(), "is_active": True }
cls 引数を使用してカスタムロジックを適用します
json_data = json.dumps(data, cls=DateTimeEncoder)
print(json_data)
### 方法 3:データの事前処理エンコーダーの動作を変更したくない場合もあります。その代わりに、dump 関数に渡す前に辞書を手動で変換することもできます。日付フィールドが1つか2つしかない場合は、この方法の方が安全なことが多いです。
from datetime import datetime import json
data = {"last_login": datetime.now()}
浅いコピーを作成し、特定のフィールドをフォーマットします
clean_data = {**data, "last_login": data["last_login"].strftime('%Y-%m-%d %H:%M:%S')}
json_output = json.dumps(clean_data)
### 方法 4:Pydantic の使用(モダンな Python)FastAPIやモダンなデータバリデーションを使用している場合は、すでに **Pydantic** がインストールされている可能性があります。Pydantic は、追加の設定なしで複雑なシリアル化をそのまま処理できます。
from pydantic import BaseModel from datetime import datetime
class Event(BaseModel): name: str timestamp: datetime
event = Event(name="System Update", timestamp=datetime.now())
Pydantic v2 では model_dump_json() を使用します
print(event.model_dump_json())
## 検証手順修正を適用したら、データが実際に使用可能であることを確認するために以下のチェックを行ってください:
- TypeError が発生せずにスクリプトが終了することを確認する。- 出力された日付文字列が、生のオブジェクト文字列ではなく `"2024-06-17T10:00:00"` のようになっていることを確認する。- JSONを再度読み込んで、有効なJSONであることを確認する:```
try:
json.loads(json_data)
print("JSONは有効であり、使用準備が整っています。")
except Exception as e:
print(f"検証失敗: {e}")

