Skip to content
ブログに戻る
チュートリアル

JSON を Python に変換:dataclass・Pydantic・TypedDict ガイド(2026)

JSON を正しく Python クラスへ変換:dataclass・Pydantic v2・TypedDict の 3 モード、Optional 型推論、camelCase エイリアス、落とし穴も解説。ブラウザ内で無料。

12 分で読める

JSON を Python に変換:dataclass・Pydantic・TypedDict ガイド(2026)

JSON を JSON to Python 変換ツール に貼り付け、生成されたクラスをコピーする。JSON を Python に変換する最短ルートはこれだ。インストール不要、アップロードもなし、すべてブラウザ内で走る。使い捨てのモデルが欲しいときや、見慣れない API レスポンスをざっと確認したいときなら、数秒で片が付く。

難しいのは、変換ツールが代わりに解いてくれない問題のほうだ。ツールは正しい型を推論できるが、一番肝心な判断だけは下せない。3 つの出力のうち、自分が本当に欲しいのはどれか、という判断だ。dataclass、Pydantic v2 の BaseModel、そして TypedDict は、まったく同じ JSON を表現できる。それでいて、型安全性の強さ、実行時の振る舞い、データの読み込み方はそれぞれ違う。選び方を間違えれば、意味もなく Pydantic の依存を抱え込むか、あるいは dataclass が入力を検証してくれると思い込んで、実際には何も検証されていない、という羽目になる。

このガイドでは、3 つすべての決定マトリクス、変換ツールがどう型を推論するか、各モードでの camelCase の扱い、生成したクラスで JSON をパースする方法(多くの人がはまるネストの罠も含む)、Pydantic v2 と v1 の違い、そして出力を信頼する前に知っておくべきエッジケースを扱う。

JSON を Python に変換する方法

JSON を Python に変換するのは 3 ステップだ。

  1. JSON を貼り付ける。 オブジェクト、配列、あるいは生の API レスポンスを入力ボックスに落とし込む。変換は即座に、完全にクライアントサイドで走る。
  2. dataclass・Pydantic・TypedDict を選ぶ。 Output のトグルで出力スタイルを切り替え、デフォルトの RootUserApiResponse のような意味のある名前に変える。
  3. コピーまたはダウンロードする。 生成された Python をワンクリックで取得し、models.py をそのままプロジェクトに落とし込む。

トランザクション的な流れはこれで全部だ。入力が minify されている、あるいは妥当か自信がない場合は、まず JSON整形ツール に通し、変換ツールがきれいで整形済みの JSON を読めるようにする。ツールが独力では推論できないケースを自分の手で直すには、出力の読み方を知っておく必要がある。

JSON の型は Python にどう対応するか

すべての JSON 値には Python の対応物があり、その対応は気持ちいいほど素直だ。

JSON 値Python 型
"text"str
42int(任意精度、オーバーフローなし)
3.14, 2e3float
true / falsebool
nullOptional[T] / Optional[Any]
[1, 2, 3]List[T]
{ ... }名前付きクラス

典型的な REST ペイロードを見てみよう。

{ "id": 101, "name": "Ada Lovelace", "email": "ada@example.com", "active": true, "roles": ["admin", "user"] }

dataclass モードでは、変換ツールはそのまま使えるクラスを生成する。

from dataclasses import dataclass
from typing import List


@dataclass
class Root:
    id: int
    name: str
    email: str
    active: bool
    roles: List[str]

ひとつだけ Python を際立たせる細部がある。Python の int は任意精度なので、Discord の snowflake や 2^53 を超えるどんな ID も、精度を失わずただの int のままでいられる。同じ値に対して、JavaScript なら精度が落ちるし、Rust なら i64u64f64 のどれかを選ばされる。float と推論されるのは、小数点や指数付きで書かれたトークンだけだ。ずっと null のままのフィールドは、サンプルだけからは型を決められないので Optional[Any] に落ちる。値の入った代表的なペイロードを貼れば、Any よりも鋭い型が得られる。

dataclass vs Pydantic v2 vs TypedDict — どれを使うべきか

これこそ、変換ツールが君に投げ返してくる選択だ。手短に言えば、依存ゼロの内部データホルダーが欲しいなら dataclass、信頼できない入力をパースまたは検証するなら Pydantic v2、すでに手元にある dict に静的な型ヒントを付けたいだけなら TypedDict に手を伸ばせばいい。

観点dataclassPydantic v2TypedDict
依存標準ライブラリpip install pydantic標準ライブラリ(typing
実行時検証なしあり — 検証して型変換するなし — ヒントのみ
実行時コスト軽い(実体のあるオブジェクト)いくらか(検証コスト)ゼロ(正体は dict
ネストの自動パースなし、手動あり、model_validate が再帰するなし、正体は dict
camelCase エイリアス元のキーを保持Field(alias=...)functional 構文
向いている用途内部データ構造API レスポンス・webhook・設定既存の dict コードへの型付け

{"id": 101, "name": "Ada Lovelace", "active": true} のような小さな形なら、3 つのターゲットはほとんど同じに見える。JSON から生成する dataclass は、標準ライブラリのオブジェクトをくれる。

from dataclasses import dataclass


@dataclass
class Root:
    id: int
    name: str
    active: bool

Pydantic 版は BaseModel に差し替わる。見た目は似ているが、実行時にその存在価値を示す。

from pydantic import BaseModel


class Root(BaseModel):
    id: int
    name: str
    active: bool

そして JSON から作る TypedDict は、新しいものを何も割り当てずに、素の dict の形を記述する。

from typing import TypedDict


class Root(TypedDict):
    id: int
    name: str
    active: bool

フィールドは同じ、しかし契約はまるで違う 3 つだ。同じサンプルから 3 つとも生成できることの価値は、ツール上で切り替えながら、仕事に合わせてモードを選べる点にある。自分の管理下にないデータをパースするなら Pydantic、データが内部のものなら dataclass、すでに引き回している dict を mypy に理解させたいだけなら TypedDict、という具合だ。

変換ツールはどうクラスを推論するか

与えたものはほぼ 3 つのルールで説明できる。オブジェクトの形ごとに 1 クラス、配列はキー単位でのマージ、そして欠損値の慎重な扱いだ。

構造の推論 — オブジェクトごとに名前付きクラス 1 つ

異なるオブジェクトの形は、それぞれ独立した名前付きクラスになる。ネストしたオブジェクトはインライン展開されず、別個の定義として引き上げられ、参照される。

{ "repo": "pydantic", "owner": { "login": "samuelcolvin", "id": 100 } }
from dataclasses import dataclass


@dataclass
class Owner:
    login: str
    id: int


@dataclass
class Root:
    repo: str
    owner: Owner

ネストした owner は独自の Owner クラスになり、フィールドから参照される。順序に注目してほしい。子クラスは、それを使う親より先に出力される。だからこのモジュールは from __future__ import annotations なしで動く。同一の形は重複排除もされるので、同じ構造を持つ 2 つのフィールドは、コピーを作らずに 1 つのクラスを共有する。

配列のマージと Optional フィールド

オブジェクトの配列を渡すと、変換ツールはそれらをキー単位でマージし、1 つの要素型にまとめる。一部の要素にはあるが他の要素には欠けているキーは Optional になる。

{ "users": [{ "id": 1, "nick": "x" }, { "id": 2 }] }
from typing import List, Optional, TypedDict


class User(TypedDict):
    id: int
    nick: Optional[str]


class Root(TypedDict):
    users: List[User]

id はすべての要素に現れるので、必須のままだ。nick は 2 人目のユーザーにないので、Optional[str] になる。単一のオブジェクトが弱いサンプルなのは、まさにこれが理由だ。ツールがフィールドを Optional と印を付けられるのは、キーがどこかで実際に欠けているのを目にしたときだけ。代表的な配列を与えれば、推論は本当の形を反映する。

Optional、None、空配列

いくつかの値は、それ単体では型を持たない。そして変換ツールはそのことに正直だ。一部の配列要素で欠けているキーは Optional[T] になる。ずっと null のままのフィールドは Optional[Any] になる。JSON の null だけでは、意図された型について何もわからないからだ。空の配列や、要素の型が混在した配列は List[Any] に落ちる。どれもバグではない。ツールが推測を控えているだけだ。それぞれの Any は、具体的な型を示すサンプルが手に入り次第、実際の型に置き換えればいい。なお、Python の任意精度 int のおかげで、大きな ID があっても、Rust や JavaScript のようにより広い数値型へ追い込まれることは決してない。

各モードでの camelCase キーの扱い

JSON のキーはしばしば camelCase だが、Python らしいフィールドは snake_case だ。ここに唯一の正解はなく、各モードはこれを別々のやり方で解決する。その違いは、出力をコピーする前に理解しておく価値がある。

dataclass。 dataclass には組み込みのエイリアス機構がない。Root(**data) を動かし続けるため、変換ツールは、キーが妥当な Python 識別子である限り、それをフィールド名としてそのまま保持する。だから publicRepospublicRepos のままだ。無理に snake_case にすれば、**dataTypeError で壊れてしまう。

Pydantic v2。 ここでは Python らしい結果が得られる。フィールドは snake_case に改名され、Field(alias=...) で正確な JSON キーへ対応づけられる。さらに model_config = ConfigDict(populate_by_name=True) が付き、フィールド名でもエイリアスでもモデルを構築できる。

from pydantic import BaseModel, ConfigDict, Field


class Root(BaseModel):
    model_config = ConfigDict(populate_by_name=True)

    login: str
    public_repos: int = Field(alias="publicRepos")
    created_at: str = Field(alias="createdAt")

これがあるからこそ、Root.model_validate(api_json) は本物の camelCase ペイロードを往復させられる一方で、コードは自然に読める。

TypedDict。 ただの識別子キーはそのまま使われる。識別子でないキーが現れた途端、変換ツールは正確なキーを保つために functional な TypedDict('User', {...}) 構文へ切り替える。これは次の節で扱う。

Python の予約語と識別子でないキー

JSON のキーはただの文字列なので、API が classfromfirst-name を送ってくるのを止めるものは何もない。これらはどれも、素の Python 属性として書けば構文エラーになる。だから変換ツールがそれらを無害化する。

Python の予約語であるキーには、末尾にアンダースコアが付く。classclass_ に、fromfrom_ になる。Pydantic モードではさらに Field(alias=...) も保持され、モデルは元のワイヤ上のキーを読み続けられる。

from pydantic import BaseModel, ConfigDict, Field


class Root(BaseModel):
    model_config = ConfigDict(populate_by_name=True)

    class_: str = Field(alias="class")
    from_: str = Field(alias="from")

ハイフン、スペース、あるいは先頭が数字のキーは、dataclass と Pydantic の各モードで妥当な識別子へ無害化される。リテラルのキーをそのまま保てるのは TypedDict だけだ。functional な形を使うので、"first-name" は手つかずで生き残る。

from typing import TypedDict

Root = TypedDict("Root", {"first-name": str, "2fa": bool})

結果は常に動作し、TypedDict モードではキーは API が送ってきたものとバイト単位で一致したまま残る。

生成したクラスで JSON をパースする

クラスを生成するのは仕事の半分だ。そこに JSON を読み込む段になると、3 つのモードははっきり分かれる。そして、よくある思い込みが一つ、ここで裏切られる。

dataclass。 平坦なオブジェクトなら、json.loadsRoot(**data) を足すだけで直接動く。

import json
from dataclasses import dataclass
from typing import List


@dataclass
class Root:
    id: int
    name: str
    roles: List[str]


data = json.loads('{"id": 101, "name": "Ada Lovelace", "roles": ["admin"]}')
root = Root(**data)
print(root.name, root.roles)

しかし dataclass は再帰しない。JSON にネストしたオブジェクトがあると、Root(**data) はトップレベルを埋めるだけで、子はどれも生成した Owner クラスではなく素の dict のまま残る。ツリー全体をパースするには、子を自分で組み立てるか、dacitepydantic.dataclasses のようなライブラリを使うか、ツールを Pydantic モードに切り替えるかだ。これは、ほとんどの変換ツールが決して触れない落とし穴だ。

Pydantic v2。 一度の呼び出しでツリー全体をパースし、検証する。

import json
from pydantic import BaseModel


class Root(BaseModel):
    id: int
    name: str


root = Root.model_validate(json.loads('{"id": 101, "name": "Ada Lovelace"}'))
print(root.name)

型の不一致は、静かに通り抜けるのではなく、明快な ValidationError を送出する。これが依存の見返りだ。モデルは実行時のバリデータなので、model_validate の呼び出しが値を返したという事実は、データが一致したことの保証になる。型チェッカーがそう信じているだけの静的な型付けとは、そこが違う。

TypedDict。 実行時には、TypedDict はただの dict だ。data: Root = json.loads(text) という注釈は、純粋に静的チェッカーのためのもの。コードが走るときには何も検証されない。本当に強制したいなら、CI で mypy を回すか、Pydantic へ移るかだ。モードを選ぶことは、要するに実行時の保証をどれだけ欲しいかを選ぶことでもある。そして契約が型を超える必要があるときは、JSON Schema 検証ガイド が、構造を端から端まで強制する方法を扱っている。

Pydantic v2 と v1 — 何が変わったか

変換ツールが出力するのは v2 の構文だ。いまだ v1 で動いているコードベースは多く、v2 の出力を v1 のプロジェクトに貼ると、ロジックではなく改名されたメソッドのところで失敗する。対応関係はこうだ。

タスクPydantic v2(ツールの出力)Pydantic v1
dict をパースmodel_validate(data)parse_obj(data)
生の JSON をパースmodel_validate_json(text)parse_raw(text)
モデルを設定model_config = ConfigDict(...)内部 class Config
dict へシリアライズmodel_dump()dict()
JSON へシリアライズmodel_dump_json()json()

エイリアスの振る舞いも厳しくなった。v2 では、フィールド名でもエイリアスでもモデルを構築するために、Field(alias=...) と並べて populate_by_name=True が必要になる。ツールがそれを含めているのはこのためだ。実践的なルールは短い。可能なら v2 にアップグレードすること。より速く、いまの開発が向かう先でもあるからだ。そうでなければ、model_validateparse_obj へ、ConfigDict を内部 Config へ書き戻せばいい。

json-to-python vs quicktype vs datamodel-code-generator vs 手書き

JSON から Python クラスを生成する唯一最良の方法はない。JSON がどこにあるか、そして何を出発点にできるかで変わる。

アプローチ向いている用途備考
オンライン変換ツール(本ツール、jsonlint、codeshack)使い捨ての変換、機密ペイロード、インストール不要サンプルから推論、完全にクライアントサイド
quicktype多言語出力、パイプラインでのコード生成同じくサンプル駆動。ただし Python はエイリアスやモード選択のサポートが薄い
datamodel-code-generatorJSON Schema や OpenAPI から Pydantic を生成入力はサンプルではなくスキーマ。手元にあればより権威がある
手書きでクラスを書く小さなペイロード、構文の学習完全に掌握できるが、退屈でずれやすい

身につける価値のある区別はこうだ。オンライン変換ツールと quicktype はどちらも JSON の サンプル から型を推論するが、datamodel-code-generator は JSON Schema や OpenAPI の仕様を信頼できる情報源として読む。探索や使い捨てにはサンプル系のツールを使い、契約がすでに存在するならスキーマ駆動のものに手を伸ばせばいい。コードベースが Python ではなく TypeScript なら、同じサンプル駆動のアプローチが JSON to TypeScript 変換ツール で使えるし、その側面は JSON to TypeScript インターフェースガイド が深く掘り下げている。強い型付けのコンパイル言語における実行時検証の観点なら、JSON to Rust 構造体ガイド がよい対照になる。serde は Rust の Pydantic だ。

JSON から Python を生成するときのよくある落とし穴

生成されたクラスは出発点にすぎない。本番のデータで出力を頼りにする前に、次の点に気をつけてほしい。

  • 1 つのサンプルで全部の形が見えることはめったにない。 Optional は、実際に食い違う配列要素からしか推論できない。代表的な配列を貼れば、オプショナル性は 1 つのたまたまのオブジェクトからの推測ではなく、正確になる。
  • dataclass は検証しない。 Root(**data) はフィールドを代入するだけ。型のチェックもしないし、何にも再帰しない。強制が必要なら Pydantic を使う。
  • ネストした JSON は Root(**data) を壊す。 埋まるのはトップレベルだけで、子は dict のまま残る。Pydantic の model_validate に切り替えるか、dacite を使う。
  • ずっと null のフィールドは Optional[Any] になる。 これは普通のオプショナルではなくプレースホルダーとして扱い、型がわかり次第、実際の型を与える。
  • v1 プロジェクトに v2 の出力。 model_validate は未定義になる。v2 にアップグレードするか、parse_obj に書き換える。
  • str のままの日付では日付演算ができない。 フィールドを datetime に切り替えるか、Pydantic なら datetime フィールドを使えば、ISO 8601 を代わりにパースしてくれる。

よくある質問

dataclass、Pydantic、TypedDict、JSON にはどれを使うべき?

依存ゼロの内部データホルダーには dataclass を使う。信頼できない入力をパースまたは検証するなら Pydantic v2 を使う。実行時に型を強制してくれるからだ。すでに手元にある dict に静的なヒントを付けたいだけで、実行時コストを避けたいなら TypedDict を使う。データが自分の管理の外から来るなら、既定で Pydantic にする。

Pydantic は実行時に JSON を検証する?

する。Root.model_validate(data) はコードの実行中に型を検証して変換し、不正な入力には ValidationError を送出する。dataclass はどちらもしない。Root(**data) はフィールドを代入するだけだ。TypedDict もどちらもしない。mypy のような静的チェッカーのためのヒントであり、実行時にはただの dict として振る舞う。

JSON が時々省くフィールドはどう型付けする?

Optional[T] として型付けする。キーが一部の配列要素にしか現れないとき、変換ツールは自動でそれを Optional と印を付ける。Pydantic では、そのフィールドに = None のような既定値を与えれば、キーが欠けても検証エラーにならない。dataclass のフィールドも field(default=None) で同じように既定値を持てる。

dataclass でネストした JSON のパースに失敗するのはなぜ?

Root(**data) が再帰しないからだ。トップレベルのフィールドは埋めるが、ネストしたオブジェクトは、生成した子クラスではなく素の dict のまま残る。ツリー全体を一度の呼び出しでパースするには、Pydantic の model_validate に切り替えるか、dacite や pydantic.dataclasses で再帰を足す。

大きな JSON 整数はどんな Python 型になる?

int だ。Python の整数は任意精度なので、snowflake や 2^53 を超える Discord ID も、精度を失わずただの int のまま残る。JavaScript のような精度の脱落に出くわすことはないし、Rust に強いられるように i64u64 のどちらかを選ぶ必要もない。

camelCase の JSON キーは各モードでどう扱われる?

dataclass は元のキーを保持するので Root(**data) がそのまま動く。Pydantic はフィールドを snake_case に改名し、Field(alias="camelKey")populate_by_name=True を足すので、本物の API データを往復させられる。TypedDict は、妥当な識別子でないキーを保つために functional な TypedDict('Name', {...}) 構文を使う。

class のような Python 予約語の JSON キーはどう扱う?

変換ツールは末尾にアンダースコアを足し、classclass_ に変えてコードを妥当にする。Pydantic モードではさらに Field(alias="class") を足してフィールドを元のワイヤ上のキーへ対応づけるので、名前は合法でありながら、モデルは本物のペイロードを読み続けられる。

オンラインの JSON to Python 変換ツールを使うとき、私の JSON は非公開?

非公開だ。変換は 100% ブラウザ内で JavaScript によって走る。トークン、ID、顧客データを含め、君の JSON はページを離れず、サーバーに送られることも決してない。ページさえ読み込まれれば、オフラインでも動く。準備ができたら、自分のペイロードで JSON to Python 変換ツールを試して みてほしい。

タグ: python json pydantic type-safety developer-tools