JSON ke Python: dataclass, Pydantic & TypedDict (Panduan 2026)
Tempelkan JSON Anda ke konverter JSON ke Python lalu salin kelas yang dihasilkannya. Itulah jalur tercepat untuk mengubah JSON menjadi Python: tidak ada yang perlu dipasang, tidak ada yang diunggah, semuanya berjalan di browser Anda. Untuk model sekali pakai atau sekadar mengintip respons API yang belum Anda kenal, prosesnya selesai dalam hitungan detik.
Masalah yang lebih sulit justru tidak bisa dipecahkan konverter untuk Anda. Ia menyimpulkan tipe yang benar, tetapi tidak bisa mengambil keputusan yang paling menentukan: mana dari tiga keluaran yang sebenarnya Anda inginkan. Sebuah dataclass, BaseModel milik Pydantic v2, dan TypedDict bisa menggambarkan JSON yang persis sama, tetapi ketiganya berbeda dalam kekuatan keamanan tipe (type-safety), perilaku saat runtime, dan cara Anda memuat data ke dalamnya. Salah pilih, dan Anda entah menanggung dependensi Pydantic tanpa alasan, atau mengira sebuah dataclass memvalidasi input Anda padahal sebenarnya tidak.
Panduan ini membahas matriks keputusan untuk ketiganya, cara konverter menyimpulkan tipe, penanganan camelCase di tiap mode, cara mem-parsing JSON dengan kelas yang Anda hasilkan (termasuk jebakan bersarang yang kerap menjerat banyak orang), Pydantic v2 versus v1, serta kasus-kasus tepi yang perlu Anda ketahui sebelum memercayai hasilnya.
Cara mengubah JSON menjadi Python
Mengubah JSON menjadi Python hanya butuh tiga langkah:
- Tempelkan JSON Anda. Masukkan objek, array, atau respons API mentah ke kotak input. Konversi berjalan seketika dan sepenuhnya di sisi klien (client-side).
- Pilih dataclass, Pydantic, atau TypedDict. Gunakan sakelar Output untuk berganti gaya keluaran, lalu ubah nama
Rootbawaan menjadi sesuatu yang bermakna sepertiUseratauApiResponse. - Salin atau unduh. Ambil kode Python yang dihasilkan dengan satu klik dan letakkan
models.pylangsung ke dalam proyek Anda.
Itulah keseluruhan jalur praktisnya. Jika input Anda terminifikasi atau Anda belum yakin ia valid, jalankan dulu lewat JSON formatter supaya konverter membaca JSON yang bersih dan berbentuk rapi. Sisa panduan ini menjelaskan keluarannya agar Anda bisa memperbaiki kasus-kasus yang tidak bisa disimpulkan sendiri oleh sebuah alat.
Bagaimana tipe JSON dipetakan ke Python
Setiap nilai JSON punya padanannya di Python, dan pemetaannya lugas:
| Nilai JSON | Tipe Python |
|---|---|
"text" | str |
42 | int (presisi arbitrer, tanpa overflow) |
3.14, 2e3 | float |
true / false | bool |
null | Optional[T] / Optional[Any] |
[1, 2, 3] | List[T] |
{ ... } | kelas bernama |
Ambil contoh payload REST yang umum:
{ "id": 101, "name": "Ada Lovelace", "email": "ada@example.com", "active": true, "roles": ["admin", "user"] }
Dalam mode dataclass, konverter menghasilkan kelas yang siap pakai:
from dataclasses import dataclass
from typing import List
@dataclass
class Root:
id: int
name: str
email: str
active: bool
roles: List[str]
Ada satu nuansa yang membedakan Python. Tipe int-nya berpresisi arbitrer, jadi sebuah snowflake Discord atau ID apa pun yang melampaui 2^53 tetap berupa int biasa tanpa kehilangan presisi. JavaScript akan kehilangan presisi pada nilai yang sama, dan Rust memaksa Anda memilih antara i64, u64, dan f64. Hanya token yang ditulis dengan titik desimal atau eksponen yang disimpulkan sebagai float. Sebuah field yang nilainya selalu null tidak bisa ditentukan tipenya hanya dari sampel, sehingga jatuh kembali menjadi Optional[Any]; tempelkan payload representatif dengan nilai yang terisi untuk mendapatkan tipe yang lebih tajam daripada Any.
dataclass vs Pydantic v2 vs TypedDict — mana yang sebaiknya Anda pakai?
Inilah pilihan yang dikembalikan konverter ke tangan Anda. Versi singkatnya: pilih dataclass ketika Anda ingin penampung data internal tanpa dependensi, Pydantic v2 ketika Anda mem-parsing atau memvalidasi input yang tidak tepercaya, dan TypedDict ketika Anda hanya butuh petunjuk tipe statis di atas dict yang sudah Anda miliki.
| Dimensi | dataclass | Pydantic v2 | TypedDict |
|---|---|---|---|
| Dependensi | Pustaka standar | pip install pydantic | Pustaka standar (typing) |
| Validasi runtime | Tidak | Ya — memvalidasi dan meng-coerce | Tidak — hanya petunjuk |
| Biaya runtime | Ringan (objek nyata) | Ada (validasi) | Nol (ia sebuah dict) |
| Parsing bersarang otomatis | Tidak, manual | Ya, model_validate rekursif | Tidak, ia sebuah dict |
| Alias camelCase | Mempertahankan key asli | Field(alias=...) | Sintaks fungsional |
| Paling cocok untuk | Struktur internal | Respons API, webhook, konfigurasi | Memberi tipe pada kode dict lama |
Ketiga target terlihat nyaris identik untuk bentuk kecil seperti {"id": 101, "name": "Ada Lovelace", "active": true}. Sebuah dataclass hasil konversi JSON ke Python memberi Anda objek dari pustaka standar:
from dataclasses import dataclass
@dataclass
class Root:
id: int
name: str
active: bool
Versi Pydantic-nya menukar dengan BaseModel, yang tampak serupa tetapi terbukti berguna saat runtime:
from pydantic import BaseModel
class Root(BaseModel):
id: int
name: str
active: bool
Dan sebuah TypedDict Python dari JSON menggambarkan bentuk dict biasa tanpa mengalokasikan apa pun yang baru:
from typing import TypedDict
class Root(TypedDict):
id: int
name: str
active: bool
Field yang sama, tiga kontrak yang sangat berbeda. Karena alat memberi ketiganya sekaligus dari satu sampel, Anda bisa berpindah-pindah di antaranya di dalam alat dan mencocokkan mode dengan kebutuhan: Pydantic ketika Anda mem-parsing data yang tidak Anda kendalikan, dataclass ketika datanya bersifat internal, dan TypedDict ketika Anda hanya ingin mypy memahami dict yang sudah Anda oper ke sana-sini.
Bagaimana konverter menyimpulkan kelas
Tiga aturan mencakup nyaris segala hal yang Anda berikan padanya: satu kelas per bentuk objek, penggabungan key demi key untuk array, dan penanganan cermat atas nilai yang hilang.
Inferensi struktural: satu kelas bernama per objek
Setiap bentuk objek yang berbeda menjadi kelas bernama tersendiri. Objek bersarang tidak disisipkan inline; ia diangkat menjadi definisi terpisah yang dirujuk:
{ "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
Objek owner yang bersarang menjadi kelas Owner tersendiri, dirujuk lewat field. Perhatikan urutannya: kelas anak dipancarkan sebelum induk yang memakainya, sehingga modul berjalan tanpa from __future__ import annotations. Bentuk yang identik juga di-deduplikasi, jadi dua field dengan struktur yang sama berbagi satu kelas alih-alih menghasilkan salinan.
Penggabungan array dan field Optional
Ketika Anda mengoper array berisi objek, konverter menggabungkannya key demi key menjadi satu tipe elemen. Sebuah key yang ada di sebagian elemen tetapi hilang dari yang lain menjadi 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 muncul di setiap elemen, jadi ia tetap wajib. nick tidak ada pada user kedua, sehingga menjadi Optional[str]. Inilah persisnya alasan satu objek tunggal merupakan sampel yang lemah: alat hanya bisa menandai sebuah field sebagai Optional ketika ia benar-benar melihat key itu hilang di suatu tempat. Berikan array yang representatif dan inferensinya akan mencerminkan bentuk yang sebenarnya.
Optional, None, dan array kosong
Beberapa nilai tidak membawa tipe apa pun dengan sendirinya, dan konverter jujur soal ini. Key yang hilang dari sebagian item array menjadi Optional[T]. Field yang nilainya selalu null menjadi Optional[Any], karena null JSON saja tidak memberi tahu apa pun tentang tipe yang dimaksud. Array kosong atau array dengan tipe elemen campuran jatuh kembali menjadi List[Any]. Tak satu pun dari ini adalah bug; itu adalah cara alat menolak menebak-nebak. Ganti setiap Any dengan tipe konkret begitu Anda punya sampel yang menunjukkannya, dan ingat bahwa int berpresisi arbitrer milik Python berarti ID besar tidak pernah memaksa Anda ke tipe numerik yang lebih lebar seperti yang terjadi di Rust atau JavaScript.
Menangani key camelCase di tiap mode
Key JSON sering kali camelCase sementara field Python yang idiomatis adalah snake_case. Tidak ada satu jawaban yang benar di sini, jadi tiap mode menyelesaikannya secara berbeda, dan perbedaan itu layak Anda pahami sebelum menyalin keluarannya.
dataclass. Sebuah dataclass tidak punya mekanisme alias bawaan. Agar Root(**data) tetap berfungsi, konverter mempertahankan key asli sebagai nama field selama ia merupakan identifier Python yang valid, jadi publicRepos tetap publicRepos. Memaksanya menjadi snake_case akan merusak **data dengan TypeError.
Pydantic v2. Di sini Anda mendapat hasil yang idiomatis. Field diubah namanya menjadi snake_case dengan Field(alias=...) yang memetakan kembali ke key JSON yang persis, ditambah model_config = ConfigDict(populate_by_name=True) sehingga Anda bisa membangun model lewat nama field maupun lewat alias:
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")
Itulah yang membuat Root.model_validate(api_json) bisa melakukan round-trip payload camelCase yang sesungguhnya sementara kode Anda tetap enak dibaca.
TypedDict. Key yang berupa identifier biasa dipakai langsung. Begitu muncul key yang bukan identifier, konverter beralih ke sintaks fungsional TypedDict('User', {...}) untuk mempertahankan key yang persis, yang dibahas di bagian berikutnya.
Kata kunci Python dan key non-identifier
Key JSON hanyalah string, jadi tidak ada yang mencegah sebuah API mengirim class, from, atau first-name. Masing-masing akan menjadi syntax error jika dipakai sebagai atribut Python mentah, sehingga konverter membersihkannya.
Key yang merupakan kata kunci Python diberi garis bawah di akhir: class menjadi class_, from menjadi from_. Dalam mode Pydantic, ia juga mempertahankan Field(alias=...) supaya model tetap membaca key asli dari jaringan:
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")
Key dengan tanda hubung, spasi, atau diawali angka dibersihkan menjadi identifier yang valid dalam mode dataclass dan Pydantic. TypedDict adalah satu-satunya mode yang bisa mempertahankan key literal, memakai bentuk fungsional sehingga "first-name" bertahan tanpa diubah:
from typing import TypedDict
Root = TypedDict("Root", {"first-name": str, "2fa": bool})
Hasilnya selalu bisa dijalankan, dan dalam mode TypedDict key tetap sama persis byte demi byte dengan yang dikirim API.
Mem-parsing JSON dengan kelas yang dihasilkan
Menghasilkan kelas baru separuh pekerjaan. Memuat JSON ke dalamnya adalah tempat ketiga mode ini berbeda tajam, dan tempat satu asumsi populer ternyata keliru.
dataclass. Untuk objek datar, json.loads plus Root(**data) langsung berfungsi:
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)
Tetapi dataclass tidak melakukan rekursi. Jika JSON punya objek bersarang, Root(**data) mengisi level teratas dan membiarkan setiap anak sebagai dict biasa, bukan kelas Owner yang Anda hasilkan. Untuk mem-parsing seluruh pohon, bangun sendiri anak-anaknya, gunakan pustaka seperti dacite atau pydantic.dataclasses, atau alihkan alat ke mode Pydantic. Inilah jebakan yang tidak pernah disebut oleh kebanyakan konverter.
Pydantic v2. Satu panggilan mem-parsing dan memvalidasi seluruh pohon:
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)
Ketidakcocokan tipe memunculkan ValidationError yang jelas alih-alih lolos diam-diam. Itulah imbalan atas dependensinya: model tersebut adalah validator runtime, jadi panggilan model_validate yang berhasil kembali merupakan jaminan bahwa data cocok, bukan sekadar keyakinan pemeriksa tipe bahwa data itu cocok.
TypedDict. Saat runtime, TypedDict adalah dict biasa. Menganotasi data: Root = json.loads(text) semata untuk pemeriksa statis; tidak ada yang divalidasi ketika kode berjalan. Jika Anda butuh penegakan yang sesungguhnya, jalankan mypy di CI atau beralih ke Pydantic. Memilih mode berarti memilih seberapa banyak jaminan runtime yang Anda inginkan, dan ketika sebuah kontrak perlu melampaui tipe, panduan validasi JSON Schema membahas cara menegakkan struktur dari ujung ke ujung.
Pydantic v2 vs v1 — apa yang berubah
Konverter memancarkan sintaks v2. Banyak basis kode masih berjalan di v1, dan menempelkan keluaran v2 ke proyek v1 akan gagal karena metode yang berganti nama, bukan karena logikanya. Berikut pemetaannya:
| Tugas | Pydantic v2 (keluaran alat) | Pydantic v1 |
|---|---|---|
| Mem-parsing dict | model_validate(data) | parse_obj(data) |
| Mem-parsing JSON mentah | model_validate_json(text) | parse_raw(text) |
| Mengonfigurasi model | model_config = ConfigDict(...) | class Config internal |
| Serialisasi ke dict | model_dump() | dict() |
| Serialisasi ke JSON | model_dump_json() | json() |
Perilaku alias juga diperketat: di v2 Anda perlu populate_by_name=True bersama Field(alias=...) untuk membangun model lewat nama field maupun lewat alias, itulah sebabnya alat menyertakannya. Aturan praktisnya singkat. Naikkan ke v2 jika bisa, karena ia lebih cepat dan menjadi sasaran pengembangan saat ini; kalau tidak, tulis ulang model_validate kembali menjadi parse_obj dan ConfigDict kembali menjadi Config internal.
json-to-python vs quicktype vs datamodel-code-generator vs manual
Tidak ada satu cara terbaik untuk menghasilkan kelas Python dari JSON. Semuanya bergantung pada di mana JSON itu berada dan dari apa Anda memulai.
| Pendekatan | Paling cocok untuk | Catatan |
|---|---|---|
| Konverter online (alat ini, jsonlint, codeshack) | Konversi sekali pakai, payload sensitif, tanpa instalasi | Menyimpulkan dari sampel, sepenuhnya di sisi klien |
| quicktype | Keluaran multi-bahasa, codegen dalam pipeline | Juga berbasis sampel; dukungan Python-nya lebih tipis soal alias dan pilihan mode |
| datamodel-code-generator | Menghasilkan Pydantic dari JSON Schema atau OpenAPI | Inputnya sebuah schema, bukan sampel — lebih otoritatif ketika Anda memilikinya |
| Menulis kelas dengan tangan | Payload kecil, belajar sintaks | Kendali penuh, tetapi merepotkan dan mudah menyimpang |
Perbedaan yang layak Anda serap: konverter online dan quicktype sama-sama menyimpulkan tipe dari sampel JSON, sementara datamodel-code-generator membaca spesifikasi JSON Schema atau OpenAPI sebagai sumber kebenarannya. Pakai alat berbasis sampel untuk eksplorasi dan pekerjaan sekali pakai; andalkan yang berbasis schema ketika sebuah kontrak sudah ada. Jika basis kode Anda TypeScript alih-alih Python, pendekatan berbasis sampel yang sama berlaku dengan konverter JSON ke TypeScript, dan panduan interface JSON ke TypeScript mengupas sisi itu secara mendalam. Untuk sudut pandang validasi runtime dalam bahasa terkompilasi yang bertipe kuat, panduan struct JSON ke Rust memberi perbandingan yang berguna: serde adalah Pydantic-nya Rust.
Jebakan umum saat menghasilkan Python dari JSON
Kelas yang dihasilkan hanyalah titik awal. Waspadai hal-hal ini sebelum Anda mengandalkan keluarannya dengan data langsung.
- Satu sampel jarang mengungkap setiap bentuk.
Optionalhanya bisa disimpulkan dari elemen array yang benar-benar berbeda. Tempelkan array yang representatif agar sifat opsionalnya akurat, bukan tebakan dari satu objek yang kebetulan. - Dataclass tidak memvalidasi.
Root(**data)hanya menugaskan field; ia tidak memeriksa tipe apa pun dan tidak melakukan rekursi ke mana pun. Jika Anda butuh penegakan, gunakan Pydantic. - JSON bersarang merusak
Root(**data). Hanya level teratas yang terisi; anak-anaknya tetap dict. Beralih kemodel_validatemilik Pydantic, atau gunakan dacite. - Field yang selalu
nullmenjadiOptional[Any]. Perlakukan sebagai placeholder, bukan opsional biasa, dan beri ia tipe yang sebenarnya begitu Anda mengetahuinya. - Keluaran v2 di proyek v1.
model_validateakan tidak terdefinisi. Naikkan ke v2 atau tulis ulang menjadiparse_obj. - Tanggal yang dibiarkan sebagai
strtidak bisa dipakai untuk hitungan tanggal. Ubah field menjadidatetime, atau di Pydantic gunakan fielddatetimesupaya ia mem-parsing ISO 8601 untuk Anda.
Pertanyaan yang sering diajukan
Sebaiknya saya pakai dataclass, Pydantic, atau TypedDict untuk JSON?
Pakai dataclass untuk penampung data internal tanpa dependensi. Pakai Pydantic v2 ketika Anda mem-parsing atau memvalidasi input yang tidak tepercaya, karena ia menegakkan tipe saat runtime. Pakai TypedDict untuk menambahkan petunjuk statis pada dict yang sudah Anda miliki, tanpa biaya runtime. Ketika data datang dari luar kendali Anda, jadikan Pydantic pilihan bawaan.
Apakah Pydantic memvalidasi JSON saat runtime?
Ya. Root.model_validate(data) memvalidasi dan meng-coerce tipe seiring kode berjalan, memunculkan ValidationError jika inputnya buruk. Dataclass tidak melakukan keduanya — Root(**data) hanya menugaskan field. TypedDict pun tidak melakukan keduanya; ia sekadar petunjuk untuk pemeriksa statis seperti mypy dan berperilaku sebagai dict biasa saat runtime.
Bagaimana cara memberi tipe pada field yang kadang tidak disertakan JSON?
Beri tipe Optional[T]. Ketika sebuah key hanya muncul di sebagian elemen array, konverter menandainya Optional secara otomatis. Di Pydantic, beri field itu nilai default seperti = None supaya key yang hilang tidak memicu error validasi. Field dataclass bisa diberi default dengan cara yang sama lewat field(default=None).
Mengapa dataclass saya gagal mem-parsing JSON bersarang?
Karena Root(**data) tidak melakukan rekursi. Ia mengisi field level teratas dan membiarkan objek bersarang sebagai dict biasa alih-alih kelas anak yang Anda hasilkan. Untuk mem-parsing seluruh pohon dalam satu panggilan, beralih ke model_validate milik Pydantic, atau tambahkan rekursi dengan dacite atau pydantic.dataclasses.
Menjadi tipe Python apa sebuah integer JSON yang besar?
int. Integer Python berpresisi arbitrer, jadi sebuah snowflake atau ID Discord yang melampaui 2^53 tetap berupa int biasa tanpa kehilangan presisi. Anda tidak pernah mengalami penurunan presisi seperti pada JavaScript, dan tidak pernah harus memilih antara i64 dan u64 seperti yang dipaksakan Rust.
Bagaimana key JSON camelCase ditangani di tiap mode?
Dataclass mempertahankan key asli sehingga Root(**data) tetap berfungsi. Pydantic mengubah nama field menjadi snake_case dengan Field(alias="camelKey") plus populate_by_name=True, sehingga bisa round-trip data API yang sesungguhnya. TypedDict memakai sintaks fungsional TypedDict('Name', {...}) untuk mempertahankan key apa pun yang bukan identifier valid.
Bagaimana cara menangani key JSON yang merupakan kata kunci Python seperti class?
Konverter menambahkan garis bawah di akhir, mengubah class menjadi class_ supaya kode tetap valid. Dalam mode Pydantic, ia juga menambahkan Field(alias="class") yang memetakan field kembali ke key asli dari jaringan, sehingga namanya sah dan model tetap membaca payload yang sesungguhnya.
Apakah JSON saya tetap privat saat memakai konverter JSON ke Python online?
Ya. Konversi berjalan 100% di browser Anda dengan JavaScript. JSON Anda — termasuk token, ID, dan data pelanggan — tidak pernah meninggalkan halaman dan tidak pernah dikirim ke server. Ia bahkan bekerja secara offline begitu halaman selesai dimuat. Ketika Anda siap, coba konverter JSON ke Python pada payload Anda sendiri.