JSON na Python: dataclass, Pydantic i TypedDict (2026)
Wystarczy wkleić JSON do konwertera JSON na Python i skopiować wygenerowane klasy. To najszybsza droga do zamiany JSON na Python: nic nie instalujesz, a JSON zostaje w przeglądarce. Przy jednorazowym modelu albo szybkim rzucie oka na nieznaną odpowiedź API sprawa jest załatwiona w kilka sekund.
Trudniejszy jest problem, którego konwerter nie rozwiąże za ciebie. Wnioskuje poprawne typy, ale nie podejmie decyzji, która liczy się najbardziej: którego z trzech wyników naprawdę chcesz. dataclass, BaseModel z Pydantic v2 oraz TypedDict mogą opisywać dokładnie ten sam JSON, a mimo to różnią się siłą bezpieczeństwa typów, zachowaniem w czasie działania i sposobem wczytywania do nich danych. Wybierz źle, a albo ciągniesz zależność od Pydantic bez powodu, albo zakładasz, że dataclass waliduje dane wejściowe, podczas gdy po cichu tego nie robi.
Ten przewodnik obejmuje macierz decyzyjną dla wszystkich trzech, sposób, w jaki konwerter wnioskuje typy, obsługę camelCase w każdym trybie, parsowanie JSON za pomocą wygenerowanej klasy (łącznie z pułapką zagnieżdżenia, na którą wielu się nabiera), Pydantic v2 kontra v1 oraz przypadki brzegowe, które warto znać, zanim zaufasz wynikowi.
Jak przekonwertować JSON na Python
Konwersja JSON na Python zajmuje trzy kroki:
- Wklej JSON. Wrzuć obiekt, tablicę lub surową odpowiedź API do pola wejściowego. Konwersja przebiega natychmiast i w całości po stronie klienta.
- Wybierz dataclass, Pydantic lub TypedDict. Użyj przełącznika Output, aby zmienić styl, i zmień domyślną nazwę
Rootna coś sensownego, np.UserlubApiResponse. - Skopiuj lub pobierz. Pobierz wygenerowany kod Pythona jednym kliknięciem i wrzuć
models.pyprosto do projektu.
To cała ścieżka transakcyjna. Jeśli dane wejściowe są zminifikowane albo nie masz pewności, że są poprawne, przepuść je najpierw przez formater JSON, aby konwerter dostał czysty, dobrze uformowany JSON do odczytu. Reszta przewodnika wyjaśnia wynik, żebyś umiał poprawić przypadki, których narzędzie nie wywnioskuje samo.
Jak typy JSON mapują się na Python
Każda wartość JSON ma swój odpowiednik w Pythonie, a mapowanie jest bezpośrednie:
| Wartość JSON | Typ Pythona |
|---|---|
"text" | str |
42 | int (dowolna precyzja, bez przepełnienia) |
3.14, 2e3 | float |
true / false | bool |
null | Optional[T] / Optional[Any] |
[1, 2, 3] | List[T] |
{ ... } | nazwana klasa |
Weźmy typowy payload REST:
{ "id": 101, "name": "Ada Lovelace", "email": "ada@example.com", "active": true, "roles": ["admin", "user"] }
W trybie dataclass konwerter tworzy gotową do użycia klasę:
from dataclasses import dataclass
from typing import List
@dataclass
class Root:
id: int
name: str
email: str
active: bool
roles: List[str]
Jeden niuans wyróżnia Pythona. Jego int ma dowolną precyzję, więc snowflake z Discorda czy dowolny identyfikator powyżej 2^53 pozostaje zwykłym int bez straty. JavaScript straciłby precyzję na tej samej wartości, a Rust kazałby ci wybierać między i64, u64 i f64. Tylko token zapisany z kropką dziesiętną albo wykładnikiem zostaje wywnioskowany jako float. Pole, które zawsze ma wartość null, nie da się otypować na podstawie samej próbki, więc trafia do Optional[Any]; wklej reprezentatywny payload z wypełnioną wartością, aby dostać coś ostrzejszego niż Any.
dataclass kontra Pydantic v2 kontra TypedDict — którego użyć?
To wybór, który konwerter zostawia tobie. W skrócie: sięgnij po dataclass, gdy chcesz wewnętrzny kontener na dane bez żadnych zależności, po Pydantic v2, gdy parsujesz lub walidujesz niezaufane dane wejściowe, a po TypedDict, gdy potrzebujesz tylko statycznych podpowiedzi typów nad słownikami, które już masz.
| Wymiar | dataclass | Pydantic v2 | TypedDict |
|---|---|---|---|
| Zależność | Biblioteka standardowa | pip install pydantic | Biblioteka standardowa (typing) |
| Walidacja w czasie działania | Nie | Tak — waliduje i konwertuje | Nie — tylko podpowiedzi |
| Koszt w czasie działania | Niski (prawdziwy obiekt) | Pewien (walidacja) | Zero (to dict) |
| Automatyczne parsowanie zagnieżdżeń | Nie, ręcznie | Tak, model_validate schodzi rekurencyjnie | Nie, to dict |
| Aliasy camelCase | Zachowuje oryginalny klucz | Field(alias=...) | Składnia funkcyjna |
| Najlepsze do | Struktur wewnętrznych | Odpowiedzi API, webhooków, konfiguracji | Typowania starego kodu na słownikach |
Wszystkie trzy cele wyglądają niemal identycznie dla małego kształtu jak {"id": 101, "name": "Ada Lovelace", "active": true}. dataclass z konwersji JSON na Python daje ci obiekt z biblioteki standardowej:
from dataclasses import dataclass
@dataclass
class Root:
id: int
name: str
active: bool
Wersja JSON na Pydantic podstawia BaseModel, który wygląda podobnie, ale zarabia na swoje utrzymanie w czasie działania:
from pydantic import BaseModel
class Root(BaseModel):
id: int
name: str
active: bool
A TypedDict w Pythonie z JSON opisuje kształt zwykłego słownika bez alokowania niczego nowego:
from typing import TypedDict
class Root(TypedDict):
id: int
name: str
active: bool
Te same pola, trzy zupełnie różne kontrakty. Wartość generowania wszystkich trzech z jednej próbki polega na tym, że możesz przełączać się między nimi w narzędziu i dopasować tryb do zadania: Pydantic, gdy parsujesz dane, których nie kontrolujesz, dataclass, gdy dane są wewnętrzne, a TypedDict, gdy chcesz tylko, aby mypy zrozumiał słownik, który już przekazujesz w kodzie.
Jak konwerter wnioskuje klasy
Trzy reguły obejmują niemal wszystko, co mu podasz: jedna klasa na kształt obiektu, scalanie klucz po kluczu dla tablic oraz ostrożne traktowanie brakujących wartości.
Wnioskowanie strukturalne: jedna nazwana klasa na obiekt
Każdy odrębny kształt obiektu staje się własną nazwaną klasą. Konwerter nie wstawia zagnieżdżonych obiektów w miejscu, lecz wyciąga je do osobnych, referowanych definicji:
{ "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
Zagnieżdżony owner staje się własną klasą Owner, referowaną przez pole. Zwróć uwagę na kolejność: konwerter emituje klasę potomną przed rodzicem, który jej używa, więc moduł działa bez from __future__ import annotations. Deduplikuje też identyczne kształty, więc dwa pola o tej samej strukturze współdzielą jedną klasę zamiast tworzyć kopie.
Scalanie tablic i pola Optional
Gdy przekazujesz tablicę obiektów, konwerter scala je klucz po kluczu w jeden typ elementu. Klucz, który występuje w niektórych elementach, a w innych go brakuje, staje się 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 występuje w każdym elemencie, więc pozostaje wymagane. nick jest nieobecne u drugiego użytkownika, więc staje się Optional[str]. Dokładnie dlatego pojedynczy obiekt to słaba próbka: narzędzie może oznaczyć pole jako Optional tylko wtedy, gdy faktycznie widzi gdzieś brakujący klucz. Podaj reprezentatywną tablicę, a wnioskowanie odzwierciedli rzeczywisty kształt.
Optional, None i puste tablice
Kilka wartości samo z siebie nie niesie żadnego typu, a konwerter jest wobec tego uczciwy. Klucz brakujący w części elementów tablicy staje się Optional[T]. Pole, które zawsze ma tylko null, staje się Optional[Any], ponieważ samo null w JSON nic nie mówi o zamierzonym typie. Pusta tablica albo taka z mieszanymi typami elementów trafia do List[Any]. Żadne z tego nie jest błędem; to narzędzie odmawiające zgadywania. Zastąp każde Any konkretnym typem, gdy tylko będziesz mieć próbkę, która go pokazuje, i pamiętaj, że int o dowolnej precyzji w Pythonie oznacza, że duży identyfikator nigdy nie zmusza cię do szerszego typu liczbowego, jak działoby się to w Rust czy JavaScript.
Obsługa kluczy camelCase w każdym trybie
Klucze JSON są często w camelCase, podczas gdy idiomatyczne pola Pythona to snake_case. Nie ma tu jednej właściwej odpowiedzi, więc każdy tryb rozwiązuje to inaczej, a różnicę warto zrozumieć, zanim skopiujesz wynik.
dataclass. dataclass nie ma wbudowanego mechanizmu aliasów. Aby Root(**data) nadal działało, konwerter zachowuje oryginalny klucz jako nazwę pola, o ile jest to poprawny identyfikator Pythona, więc publicRepos pozostaje publicRepos. Wymuszenie na nim snake_case zepsułoby **data błędem TypeError.
Pydantic v2. Tutaj dostajesz wynik idiomatyczny. Pola są przemianowywane na snake_case z mapowaniem Field(alias=...) z powrotem na dokładny klucz JSON, plus model_config = ConfigDict(populate_by_name=True), dzięki czemu możesz budować model po nazwie pola albo po aliasie:
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")
To właśnie pozwala, aby Root.model_validate(api_json) obsługiwał w obie strony prawdziwe payloady w camelCase, podczas gdy kod czyta się naturalnie.
TypedDict. Zwykłe klucze będące identyfikatorami są używane wprost. Gdy tylko pojawi się klucz niebędący identyfikatorem, konwerter przełącza się na składnię funkcyjną TypedDict('User', {...}), aby zachować dokładny klucz, co omawia następna sekcja.
Słowa kluczowe Pythona i klucze niebędące identyfikatorami
Klucze JSON to zwykłe łańcuchy znaków, więc nic nie powstrzymuje API przed wysłaniem class, from czy first-name. Każdy z nich jako goły atrybut Pythona byłby błędem składni, więc konwerter je sanityzuje.
Klucz będący słowem kluczowym Pythona dostaje końcowe podkreślenie: class staje się class_, from staje się from_. W trybie Pydantic zachowuje też Field(alias=...), aby model nadal odczytywał oryginalny klucz z sieci:
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")
Klucze z myślnikami, spacjami albo cyfrą na początku są sanityzowane do poprawnych identyfikatorów w trybach dataclass i Pydantic. TypedDict to jedyny tryb, który potrafi zachować dosłowny klucz, korzystając z formy funkcyjnej, więc "first-name" przetrwa nietknięty:
from typing import TypedDict
Root = TypedDict("Root", {"first-name": str, "2fa": bool})
Wynik zawsze się wykonuje, a w trybie TypedDict klucz pozostaje bajt w bajt tym, co wysłało API.
Parsowanie JSON za pomocą wygenerowanej klasy
Wygenerowanie klasy to połowa roboty. Wczytywanie do niej JSON to moment, w którym trzy tryby ostro się rozchodzą i w którym jedno popularne założenie okazuje się błędne.
dataclass. Dla płaskiego obiektu json.loads plus Root(**data) działa wprost:
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)
Ale dataclass nie schodzi rekurencyjnie. Jeśli JSON ma zagnieżdżone obiekty, Root(**data) wypełnia najwyższy poziom, a każdego potomka zostawia jako zwykły dict, a nie wygenerowaną klasę Owner. Aby sparsować całe drzewo, zbuduj potomków samodzielnie, użyj biblioteki takiej jak dacite albo pydantic.dataclasses, lub przełącz narzędzie w tryb Pydantic. To pułapka, o której większość konwerterów nigdy nie wspomina.
Pydantic v2. Jedno wywołanie parsuje i waliduje całe drzewo:
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)
Niezgodność typów zgłasza czytelny ValidationError zamiast przejść po cichu. To zwrot z zależności: model jest walidatorem działającym w czasie wykonania, więc wywołanie model_validate, które zwraca wynik, jest gwarancją, że dane pasowały, a nie tylko przekonaniem kontrolera typów, że tak było.
TypedDict. W czasie działania TypedDict to zwykły dict. Adnotacja data: Root = json.loads(text) służy wyłącznie kontrolerowi statycznemu; przy uruchomieniu kodu nic nie jest walidowane. Jeśli potrzebujesz prawdziwego egzekwowania, uruchom mypy w CI albo przejdź na Pydantic. Wybór trybu to wybór, ile gwarancji w czasie działania chcesz, a gdy kontrakt musi wyjść poza typy, przewodnik po walidacji JSON Schema omawia egzekwowanie struktury od początku do końca.
Pydantic v2 kontra v1 — co się zmieniło
Konwerter emituje składnię v2. Sporo baz kodu wciąż działa na v1, a wklejenie wyniku v2 do projektu na v1 zawodzi na przemianowanych metodach, a nie na logice. Oto mapowanie:
| Zadanie | Pydantic v2 (wynik narzędzia) | Pydantic v1 |
|---|---|---|
| Parsowanie dict | model_validate(data) | parse_obj(data) |
| Parsowanie surowego JSON | model_validate_json(text) | parse_raw(text) |
| Konfiguracja modelu | model_config = ConfigDict(...) | wewnętrzna class Config |
| Serializacja do dict | model_dump() | dict() |
| Serializacja do JSON | model_dump_json() | json() |
Zachowanie aliasów też się zacieśniło: w v2 potrzebujesz populate_by_name=True obok Field(alias=...), aby zbudować model po nazwie pola albo po aliasie, i dlatego narzędzie to dołącza. Praktyczna reguła jest krótka. Przejdź na v2, jeśli możesz, bo jest szybszy i to on jest celem bieżącego rozwoju; w przeciwnym razie przepisz model_validate z powrotem na parse_obj, a ConfigDict z powrotem na wewnętrzną Config.
json-to-python kontra quicktype kontra datamodel-code-generator kontra ręcznie
Nie ma jednego najlepszego sposobu, aby wygenerować klasę Pythona z JSON. Zależy to od tego, gdzie żyje JSON i od czego zaczynasz.
| Podejście | Najlepsze do | Uwaga |
|---|---|---|
| Konwerter online (to narzędzie, jsonlint, codeshack) | Jednorazowych konwersji, wrażliwych payloadów, zera instalacji | Wnioskuje z próbki, w całości po stronie klienta |
| quicktype | Wyniku w wielu językach, generowania kodu w potoku | Też sterowany próbką; wsparcie dla Pythona jest uboższe w aliasach i wyborze trybu |
| datamodel-code-generator | Generowania Pydantic ze schematu JSON lub OpenAPI | Wejściem jest schemat, nie próbka — bardziej autorytatywny, gdy go masz |
| Pisanie klas ręcznie | Malutkich payloadów, nauki składni | Pełna kontrola, ale żmudne i łatwo o rozjazd |
Rozróżnienie warte przyswojenia: konwerter online i quicktype oba wnioskują typy z próbki JSON, podczas gdy datamodel-code-generator czyta JSON Schema lub specyfikację OpenAPI jako swoje źródło prawdy. Używaj narzędzi sterowanych próbką do eksploracji i jednorazówek; sięgaj po sterowane schematem, gdy kontrakt już istnieje. Jeśli twoja baza kodu to TypeScript zamiast Pythona, to samo podejście sterowane próbką stosuje się przy konwerterze JSON na TypeScript, a przewodnik po interfejsach JSON na TypeScript omawia tę stronę szczegółowo. Jeśli chodzi o walidację w czasie działania w silnie typowanym języku kompilowanym, przewodnik po strukturach JSON na Rust daje przydatny kontrast: serde to Pydantic Rusta.
Częste pułapki przy generowaniu Pythona z JSON
Wygenerowane klasy to punkt wyjścia. Zwróć uwagę na poniższe, zanim zaufasz wynikowi na danych produkcyjnych.
- Jedna próbka rzadko ujawnia każdy kształt.
Optionalda się wywnioskować tylko z elementów tablicy, które faktycznie się różnią. Wklej reprezentatywną tablicę, aby opcjonalność była trafna, a nie zgadywana z jednego szczęśliwego obiektu. - dataclass nie waliduje.
Root(**data)tylko przypisuje pola; nie sprawdza żadnych typów i w nic nie schodzi rekurencyjnie. Jeśli potrzebujesz egzekwowania, użyj Pydantic. - Zagnieżdżony JSON psuje
Root(**data). Wypełniany jest tylko najwyższy poziom; potomkowie pozostają słownikami. Przełącz się namodel_validatez Pydantic albo użyj dacite. - Pole zawsze równe
nullstaje sięOptional[Any]. Traktuj je jako placeholder, nie zwykłe opcjonalne, i nadaj mu prawdziwy typ, gdy tylko go poznasz. - Wynik v2 w projekcie na v1.
model_validatebędzie niezdefiniowane. Przejdź na v2 albo przepisz to naparse_obj. - Data zostawiona jako
strnie policzy różnic dat. Zmień pole nadatetimealbo w Pydantic użyj poladatetime, aby samo sparsowało dla ciebie ISO 8601.
Najczęściej zadawane pytania
Czy użyć dataclass, Pydantic czy TypedDict dla JSON?
Użyj dataclass jako wewnętrznego kontenera na dane bez zależności. Użyj Pydantic v2, gdy parsujesz lub walidujesz niezaufane dane wejściowe, bo egzekwuje typy w czasie działania. Użyj TypedDict, aby dodać statyczne podpowiedzi do słownika, który już masz, bez kosztu w czasie działania. Gdy dane pochodzą spoza twojej kontroli, domyślnie wybieraj Pydantic.
Czy Pydantic waliduje JSON w czasie działania?
Tak. Root.model_validate(data) waliduje i konwertuje typy w trakcie działania kodu, zgłaszając ValidationError przy złych danych wejściowych. dataclass nie robi ani jednego, ani drugiego — Root(**data) tylko przypisuje pola. TypedDict również nie robi żadnego z tych dwóch; to podpowiedź dla kontrolerów statycznych takich jak mypy, a w czasie działania zachowuje się jak zwykły dict.
Jak otypować pole, które JSON czasem pomija?
Otypuj je jako Optional[T]. Gdy klucz pojawia się tylko w części elementów tablicy, konwerter oznacza go jako Optional automatycznie. W Pydantic nadaj polu wartość domyślną, np. = None, aby brakujący klucz nie wywoływał błędu walidacji. Pole dataclass może mieć wartość domyślną w ten sam sposób, z field(default=None).
Dlaczego moja dataclass nie sparsowała zagnieżdżonego JSON?
Bo Root(**data) nie schodzi rekurencyjnie. Wypełnia pola najwyższego poziomu, a zagnieżdżone obiekty zostawia jako zwykłe słowniki zamiast wygenerowanych klas potomnych. Aby sparsować całe drzewo jednym wywołaniem, przełącz się na model_validate z Pydantic albo dodaj rekurencję z dacite lub pydantic.dataclasses.
Jakim typem Pythona staje się duża liczba całkowita z JSON?
int. Liczby całkowite w Pythonie mają dowolną precyzję, więc snowflake albo identyfikator Discorda powyżej 2^53 pozostaje zwykłym int bez straty. Nigdy nie trafiasz na spadek precyzji, jaki ma JavaScript, i nigdy nie musisz wybierać między i64 a u64, jak zmusza cię do tego Rust.
Jak klucze JSON w camelCase są obsługiwane w każdym trybie?
dataclass zachowuje oryginalny klucz, więc Root(**data) nadal działa. Pydantic przemianowuje pola na snake_case z Field(alias="camelKey") plus populate_by_name=True, więc obsługuje w obie strony prawdziwe dane API. TypedDict używa składni funkcyjnej TypedDict('Name', {...}), aby zachować każdy klucz, który nie jest poprawnym identyfikatorem.
Jak obsłużyć klucz JSON, który jest słowem kluczowym Pythona, np. class?
Konwerter dodaje końcowe podkreślenie, zamieniając class na class_, aby kod był poprawny. W trybie Pydantic dodaje też Field(alias="class"), mapując pole z powrotem na oryginalny klucz z sieci, więc nazwa jest legalna, a model nadal odczytuje prawdziwy payload.
Czy mój JSON jest prywatny podczas korzystania z konwertera JSON na Python online?
Tak. Konwersja odbywa się w 100% w przeglądarce za pomocą JavaScriptu. JSON — łącznie z tokenami, identyfikatorami i danymi klientów — nigdy nie opuszcza strony i nigdy nie jest wysyłany na serwer. Działa nawet offline, gdy strona już się załaduje. Gdy będziesz gotowy, wypróbuj konwerter JSON na Python na własnym payloadzie.