メインコンテンツへ移動

JSONをPythonに変換

「JSONをPythonに変換」は、サンプルのドキュメントからデータクラス、TypedDict、Pydanticモデルをブラウザ内で生成します。3つは異なる問いに答えます。データクラスは依存関係なしにデータを記述し、TypedDictはjson.loadsが既に返した辞書を記述し、Pydanticモデルは境界でそれを検証します。

端末内で処理
形式
サンプルJSON
0 B1 行行 1、列 1
構文エラー · 1行1列でエラー

ドキュメントが途中で終わっています。切れている可能性があります。


^
生成されたコード読み取り専用
0 B1 行
複数のレコードがあれば貼り付けてください。一部の行にだけあるフィールドを検出し、正しい省略可能な型を生成できます。

JSONをPythonに変換はどう動きますか?

形は、結果をどう使うつもりかで選んでください。3つは入れ替え可能ではなく、間違ったものを選ぶことが、生成されたPythonで最もよくある問題です。

データクラス、TypedDict、Pydanticモデル

@dataclassは、属性アクセス、等価性、有用なreprを備えた本物のオブジェクトを、サードパーティの依存なしで与えます。ただし検証は何もしないので、intと注釈したフィールドでも、構築時に渡された文字列をそのまま保持します。TypedDictは、json.loadsが既に返した素の辞書に注釈を付けるだけなので、何も構築する必要がなく、型チェッカーはキーの打ち間違いを依然として捕捉します。パース済みのJSONをそのまま受け渡す場合には、これが正解です。PydanticのBaseModelは、構築時に実際に検証と型変換を行います。APIクライアントが境界で求める振る舞いですが、依存関係と実行時の作業のコストが掛かります。

フィールドの順序はスタイルではなく正しさの問題

デフォルト値を持つデータクラスのフィールドは、持たないフィールドより前に置けません。Pythonは、最初に使うときではなく、importの時点でTypeErrorを投げるからです。省略可能なフィールドには=Noneが付くため、このジェネレーターは必須のフィールドをすべて先に出力します。ここでJSONのキー順を保つジェネレーターは、importに失敗するモジュールを出力することになります。

Optionalは「Noneになり得る」の意味であり、「省略できる」の意味ではない

特にPythonでは、ここでつまずく人が多いです。Optional[int]はoptional(省略可能)と読めて、実際の意味はnullable(nullになり得る)だからです。データクラスでは、一部のレコードで欠落するフィールドはOptional[int] = Noneとして出力されます。nullになり得るかは注釈が、欠落するかはデフォルト値が担います。TypedDictでは2つは本当に別の概念で、存在しないキーはNotRequired[int]、nullの値はOptional[int]になります。

改名したキーはエイリアスを残す

Pythonの慣習はsnake_caseですが、JavaScriptのサービスが出すJSONはたいてい違います。キーは改名され、元の名前は記録されます。データクラスやTypedDictではコメントとして、Pydanticでは本物のField(alias=…)としてです。Pydanticでは、記録するだけでなく動作が変わります。

前方参照はクォートする

ネストしたクラスは、それを使うクラスより先に出力されますが、注釈は文字列として書きます。手で並べ替えても、モジュールがimportできる状態が保たれます。

サンプル

[
  { "userId": 1, "team": "ops" },
  { "userId": 2 }
]

生成結果

from dataclasses import dataclass
from typing import Optional

@dataclass
class User:
    user_id: int  # JSON key: userId
    team: Optional[str] = None

JSONをPythonに変換が対応するオプションとエッジケースは?

技術リファレンスとオプション
パラメータ型デフォルト動作とエッジケース
形状dataclass / TypedDict / pydanticdataclass依存関係のないオブジェクト用のdataclass; json.loadsが返すdictを注釈するTypedDict; 境界で値を検証したい場合のpydantic。
snake_case真偽値onキーをPythonの規約に名前を変更します。pydanticでは実際のエイリアスになるため、パースはワイヤ形式と一致します。それ以外の場所ではコメントとして記録され、キーは自分でマッピングする必要があります。
X | None真偽値offOptional[X]の代わりにPython 3.10+の共用構文を使用します。より短く、typingのインポートを削除しますが、Python 3.9以下では構文エラーになります。
stringPython型str検出されたフォーマットはdatetimeやUUIDにマッピングされません: json.loadsは文字列を返し、何かがそれをパースしない限り、datetimeとして注釈をつけることは嘘になります。
整数 / 数値Python型int / floatサンプルに小数部分があるかどうかで分けられます。Pythonの整数は任意精度なので、ここにint64の区別はありません。
任意フィールドアノテーションOptional[T] = NoneTypedDictでは、存在しないキーはNotRequired[T]になり、これはPythonが区別するもので、他の2つの形式ではできません。
フィールド順序必須を先頭強制デフォルト値のないdataclassフィールドは、デフォルト値のあるフィールドの後に続けることはできません。必須フィールドはJSONキーの順序に関係なく最初に出力されます。
ネストしたオブジェクトクラスクォート付き参照独自のクラスに昇格し、使用前に定義され、引用符付きの前方参照で参照されるため、モジュールは並べ替えを失いません。

よくある質問

データクラス、TypedDict、Pydanticのどれを選ぶべきですか?

データが信頼の境界を越える場合——HTTPレスポンス、Webhook、誰かがアップロードしたファイル——にはPydanticを使ってください。3つのうち、実際に何かを検証するのはこれだけです。既にパースした辞書に注釈を付け、社内で受け渡すだけなら、実行時のコストがゼロのTypedDictを使います。本物のオブジェクトが欲しく、依存関係を増やしたくない場合はデータクラスです。ただし、注釈は保証ではなく、型チェッカーのためのドキュメントだと受け入れてください。

フィールドの順序がJSONと違うのはなぜですか?

デフォルト値を持つデータクラスのフィールドは、持たないフィールドより後に置けないからです。Pythonは、あなたのコードが1行も実行される前に、モジュールがimportされる時点でTypeErrorを投げます。省略可能なフィールドには=Noneが付くので、必須のフィールドの後ろへ全部移動されます。TypedDictとPydanticにこのルールはなく、元の順序が保たれます。

Optional[int]はキーが欠落していてもよいという意味ですか?

いいえ。そしてこれが、Pythonの型付けで最もよくある誤読です。Optional[int]が意味するのは、値がNoneかもしれないことです。キーが存在しなくてもよいかは別の問いで、データクラスではデフォルト値が、TypedDictではNotRequiredが表現します。このジェネレーターは両者を区別します。一部のレコードで欠落するキーにはデフォルト値を付け、一部のレコードでnullになる値にはOptionalを付けます。

タイムスタンプがdatetimeではなくstrになるのはなぜですか?

json.loadsは文字列を返します。パースする処理がない限り、datetimeと注釈するのは嘘になります。Pydanticなら注釈をdatetimeに変えればISO 8601をパースしてくれます。Pydanticを選ぶ良い理由のひとつです。データクラスやTypedDictでは、注釈を変えても実行時には何も変わらず、読む人を皆誤導します。

生成されたTypedDictをPython 3.9で使えますか?

はい。importがtypingではなくtyping_extensionsからになっているのは、そのためです。NotRequiredが標準ライブラリに入ったのは3.11からですが、typing_extensionsはTypedDictが存在するすべてのバージョンへバックポートしています。逆に、X | Noneオプションを有効にするには3.10が必要です。

JSONはアップロードされますか?

いいえ。パース、推論、生成はすべてブラウザの中で行われます。Networkパネルで確認できます。サンプルからクラスを生成するのにサーバーは必要なく、貼り付けられるサンプルは、たいてい統合相手のシステムからの実際のペイロードです。