跳至內容

JSON 轉 Python

從 JSON 範例生成 Python dataclass 或 TypedDict。

輸入

這個工具的用途

從 JSON 樣本產生帶型別註解的 Python dataclass。每個巢狀物件成為各自的 dataclass、可為 null 的欄位使用 Optional[T],輸出附帶 from-dict 轉換器,讓你能 json.loads() 後直接傳入。底層使用 quicktype, 完全在你的瀏覽器內運作。

使用步驟

貼上 JSON(或載入範例),即可在右側讀到 Python dataclass。每個巢狀 物件成為各自的 dataclass,帶型別註解;輸出相容於 Python 3.7+。

輸入: {"id":42,"name":"devsmiths","createdAt":"2024-03-11T08:24:00Z","stars":1280,"public":true,"contributors":[{"login":"ada","commits":51,"admin":true},{"login":"linus","commits":33,"admin":false}],"homepage":null}

輸出(Python):

from dataclasses import dataclass
from typing import Any, List, Optional


@dataclass
class Contributor:
    login: str
    commits: int
    admin: bool


@dataclass
class Root:
    id: int
    name: str
    created_at: str
    stars: int
    public: bool
    contributors: List[Contributor]
    homepage: Optional[Any]

限制與邊界情況

  • 輸出是 @dataclass 帶型別註解,不是 Pydantic BaseModelTypedDict。dataclass 屬於 stdlib (Python 3.7+)且 runtime-free。若需要驗證,可用 Pydantic 的 pydantic.dataclasses.dataclass 包裹現有的 dataclass。
  • camelCase JSON 鍵會轉成 snake_case 欄位(createdAtcreated_at)。 這會打斷 json.loads() + **dict 建構式—— 請使用內附的 from_dict 轉換器,或用 cattrs 處理重新命名。
  • 可為 null 的欄位使用 Optional[T](即 Union[T, None]),不是 PEP 604 的 T | None。 若在 Python 3.10+ 且偏好新語法,可透過 pyupgrade 轉換。
  • 保留字與 dunder 名稱在 JSON 鍵中會加上底線字尾(class_ type_),原始拼法保留在 __post_init__ 的 rename map 中。
  • 日期 / datetime 字串會保持為 str;產生器無法分辨 ISO 8601 與一般字串。可在 post-init hook 中以 datetime.fromisoformat() 轉換,或用 cattrs hooks。
  • 單樣本推論限制適用:樣本中不存在的鍵不會自動變為 Optional

常見問題

為什麼用 dataclass 而不是 Pydantic BaseModel?
dataclass 屬於 stdlib(無 runtime 相依),輸出可用於任何 Python 3.7+ 專案。Pydantic 適合 runtime 驗證但會加上約 5 MB 相依樹,且語意不同(Pydantic 會強制轉型,dataclass 不會)。升級方式:把每個 `@dataclass` 用 `pydantic.dataclasses.dataclass` 包起來——欄位語法一致,建構時帶上 Pydantic 驗證。
如何把 JSON 字串解析成產生的 dataclass?
使用產生器附帶的 from-dict 轉換器:`Root.from_dict(json.loads(payload))`。純 stdlib `json.loads()` 回傳 `dict`,不是 `Root`。對巢狀物件,from-dict 會遞迴處理。若偏好 cattrs(可設定性更強),dataclass 形狀相容——傳入 `cattrs.structure(payload_dict, Root)` 即可。
為什麼我的 JSON 鍵 createdAt 被改成 created_at?
PEP 8 建議 Python 變數名為 snake_case。產生器在 Python 端遵循 PEP 8,但 JSON 序列化需要知道原始鍵。from-dict 轉換器負責重命名;若用其他反序列化器(Pydantic、marshmallow),請設定 `populate_by_name = True` 與 `alias = 'createdAt'`,或在傳入前轉換 dict 鍵。
可以用 PEP 604 聯合語法(X | None)取代 Optional[X] 嗎?
產生器本身不支援。可把輸出丟給 `pyupgrade --py310-plus` 將 `Optional[X]` 轉成 `X | None`。語意完全相同;PEP 604 只是較簡潔的語法,要求 Python 3.10+。
Python 保留字(class、def、type)當 JSON 鍵時怎麼辦?
會加上底線字尾(`class_`、`def_`、`type_`),原始鍵保留在 `__post_init__` 的 rename map 中供 from-dict 反序列化使用。rename map 是 Python 中處理這種情況最乾淨的方式——其他做法(透過 `__getattr__` 暴露原名)可行但帶 runtime 成本。
為什麼貨幣欄位不會用 decimal.Decimal?
因為 JSON 規格只有一種數字型別,也沒辦法標記「這個值應保持 decimal」。請為貨幣欄位手動轉換,或使用支援無損數字保留的 JSON parser(`simplejson` 配 `use_decimal=True`)並把 dataclass 欄位型別改為 `Decimal`。產生器預設保持 float。

內容審閱者: