ネストされたJSONオブジェクトからPython Dataclassへの変換
ネストされたサブオブジェクトを含むJSONオブジェクトから複数のPython dataclassを生成する方法を学びます。命名、合成、デシリアライゼーションのコツを含みます。
Dataclasses
詳細な説明
DataclassによるネストされたJSON構造の処理
実際のJSONはフラットであることはまれです。APIはオブジェクトの中にオブジェクトを返し、各ネストレベルはPythonでは別のdataclassにマッピングされます。
JSONの例
{
"id": 1,
"name": "Acme Corp",
"address": {
"street": "123 Main St",
"city": "Springfield",
"geo": {
"lat": 39.7817,
"lng": -89.6501
}
}
}
生成されるPython
from dataclasses import dataclass
@dataclass
class Geo:
lat: float
lng: float
@dataclass
class Address:
street: str
city: str
geo: Geo
@dataclass
class Company:
id: int
name: str
address: Address
なぜ別々のDataclassにするのか?
各ネストされたオブジェクトを独自のdataclassに抽出することで:
- 再利用性 —
Geoは座標が登場するすべての場所(店舗、イベント、配送先)で再利用できます。 - 可読性 — 深くネストされたdictは理解しにくいですが、名前付きクラスは自己文書化されます。
- テスト容易性 —
AddressをCompanyから独立してインスタンス化しテストできます。
デシリアライゼーション
json.loads() はネストされたdictを返しますが、dataclassインスタンスは返しません。再帰的な構築ステップが必要です:
import json
data = json.loads(raw_json)
company = Company(
id=data["id"],
name=data["name"],
address=Address(
street=data["address"]["street"],
city=data["address"]["city"],
geo=Geo(**data["address"]["geo"]),
),
)
dacite や cattrs などのライブラリでこれを自動化できます:
from dacite import from_dict
company = from_dict(data_class=Company, data=data)
宣言順序
Pythonでは、参照されるクラスはそれを使用するクラスの前に定義する必要があります(または from __future__ import annotations を使用)。コンバーターはリーフクラスの Geo を最初に、次に Address、最後に Company を配置します。
ユースケース
サードパーティAPIが住所やジオロケーションのサブオブジェクトを含む深くネストされた企業データを返し、mypyバリデーションによる各レベルへの型安全なアクセスが必要な場合に使用します。