JSONからPython dataclassジェネレーター
JSONからPythonのdataclassを自動生成
JSON入力
Python出力
JSONからPython dataclassへの変換とは?
JSONからPython dataclassへの変換は、生のJSONオブジェクトを受け取り、正確な型アノテーション付きのPython dataclass定義セットを生成します。PEP 557(Python 3.7)で導入されたPythonのdataclassesモジュールは、アノテーション付きクラスフィールドから__init__、__repr__、__eq__メソッドを自動生成します。JSON API・設定ファイル・メッセージキューを扱う際、dataclassはデータに型付き構造を与え、エディタやmypyのような型チェッカーが開発時に検証できるようにします。
Pythonのjson.loads()はプレーンなdictとlistを返します。これで動作はしますが、型情報がありません。スペルミスのあるキーはエラーを発生させずにNoneを返し、エディタはフィールド名を補完できません。dataclassはJSONの各キーを名前付きの型付きフィールドにマッピングすることでこの問題を解決します。ネストされたJSONオブジェクトは個別のdataclass定義になり、配列はList[T]アノテーションになり、null値はデフォルト値Noneを持つOptional[T]になります。
これらの定義を手書きするのは機械的な作業です。JSONを読み、各フィールドの型を値から判断し、キーをcamelCaseやsnake_caseからPythonの慣習に変換し、nullableフィールドや混在型配列などのエッジケースを処理する必要があります。変換ツールはこれをミリ秒以内に行います。JSONを貼り付けると正しいdataclassのコードが得られ、すぐに作業を進められます。
JSONからPython変換ツールを使う理由
JSON構造をPythonクラス定義に手動で変換するには、サンプルデータから型を推測し、必須フィールドをオプションフィールドより前に並べ、APIが変更されたらすべてを更新する作業が伴います。変換ツールはその手間を取り除きます。
JSONからPythonへの変換ユースケース
JSONからPythonへの型マッピング
JSONの各値は特定のPython型アノテーションに対応します。下表はtypingモジュールの構文(Python 3.7以上)とPython 3.10以降で利用可能な組み込み構文の両方を示しています。
| JSON型 | 値の例 | Python(typing) | Python 3.10以降 |
|---|---|---|---|
| string | "hello" | str | str |
| number (integer) | 42 | int | int |
| number (float) | 3.14 | float | float |
| boolean | true | bool | bool |
| null | null | Optional[str] | str | None |
| object | {"k": "v"} | @dataclass class | nested model |
| array of strings | ["a", "b"] | List[str] | list[str] |
| array of objects | [{"id": 1}] | List[Item] | list[Item] |
| mixed array | [1, "a"] | List[Any] | list[Any] |
@dataclassデコレーターリファレンス
@dataclassデコレーターは、生成されたクラスの動作を変えるいくつかのパラメーターを受け取ります。このリファレンスはJSON由来のデータを扱う際に特に関連するオプションを説明します。
| デコレーター / フィールド | 動作 | 使用場面 |
|---|---|---|
| @dataclass | Generates __init__, __repr__, __eq__ from field annotations | Standard dataclasses |
| @dataclass(frozen=True) | Makes instances immutable (hashable, no attribute reassignment) | Config objects, dict keys |
| @dataclass(slots=True) | Uses __slots__ for lower memory and faster attribute access | Python 3.10+, large datasets |
| @dataclass(kw_only=True) | All fields require keyword arguments in __init__ | Python 3.10+, many fields |
| field(default_factory=list) | Sets a mutable default without sharing state between instances | List/dict/set defaults |
dataclass vs Pydantic vs TypedDict
PythonにはJSONから型付き構造を定義する3つの一般的な方法があります。それぞれ異なるユースケースに対応しています。dataclassは依存関係ゼロの標準ライブラリの選択肢です。Pydanticは実行時バリデーションを追加します。TypedDictは新しいクラスを作らずにプレーンなdictにアノテーションを付けます。
コード例
以下の例は、生成されたdataclassをPythonで使う方法、JavaScriptからプログラムで生成する方法、PydanticやCLIツールなどの代替手段を示しています。
from dataclasses import dataclass
from typing import List, Optional
import json
@dataclass
class Address:
street: str
city: str
zip: str
@dataclass
class User:
id: int
name: str
email: str
active: bool
score: float
address: Address
tags: List[str]
metadata: Optional[str] = None
raw = '{"id":1,"name":"Alice","email":"alice@example.com","active":true,"score":98.5,"address":{"street":"123 Main St","city":"Springfield","zip":"12345"},"tags":["admin","user"],"metadata":null}'
data = json.loads(raw)
# Reconstruct nested objects manually
addr = Address(**data["address"])
user = User(**{**data, "address": addr})
print(user.name) # -> Alice
print(user.address) # -> Address(street='123 Main St', city='Springfield', zip='12345')// Minimal JSON-to-Python-dataclass generator in JS
function jsonToPython(obj, name = "Root") {
const classes = [];
function infer(val, fieldName) {
if (val === null) return "Optional[str]";
if (typeof val === "string") return "str";
if (typeof val === "number") return Number.isInteger(val) ? "int" : "float";
if (typeof val === "boolean") return "bool";
if (Array.isArray(val)) {
const first = val.find(v => v !== null);
return first ? `List[${infer(first, fieldName + "Item")}]` : "List[Any]";
}
if (typeof val === "object") {
const clsName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
build(val, clsName);
return clsName;
}
return "Any";
}
function build(obj, cls) {
const fields = Object.entries(obj).map(([k, v]) => ` ${k}: ${infer(v, k)}`);
classes.push(`@dataclass\nclass ${cls}:\n${fields.join("\n")}`);
}
build(obj, name);
return classes.join("\n\n");
}
const data = { id: 1, name: "Alice", scores: [98, 85] };
console.log(jsonToPython(data, "User"));
// @dataclass
// class User:
// id: int
// name: str
// scores: List[int]from pydantic import BaseModel
from typing import List, Optional
class Address(BaseModel):
street: str
city: str
zip: str
class User(BaseModel):
id: int
name: str
email: str
active: bool
score: float
address: Address
tags: List[str]
metadata: Optional[str] = None
# Pydantic parses and validates JSON in one step
raw = '{"id":1,"name":"Alice","email":"alice@example.com","active":true,"score":98.5,"address":{"street":"123 Main St","city":"Springfield","zip":"12345"},"tags":["admin","user"],"metadata":null}'
user = User.model_validate_json(raw)
print(user.name) # -> Alice
print(user.model_dump_json()) # -> re-serializes to JSON# Install the generator
pip install datamodel-code-generator
# Generate dataclasses from a JSON file
datamodel-codegen --input data.json --output models.py --output-model-type dataclasses.dataclass
# Generate Pydantic models instead
datamodel-codegen --input data.json --output models.py
# From a JSON string via stdin
echo '{"id": 1, "name": "Alice", "tags": ["admin"]}' | \
datamodel-codegen --output-model-type dataclasses.dataclass
# Output:
# @dataclass
# class Model:
# id: int
# name: str
# tags: List[str]