Method .json() terasa seperti mantra: satu panggilan, dan data API berubah menjadi dictionary Python. Mantra itu berhenti menyenangkan begitu responsnya punya lima tingkat struktur bersarang, atau begitu key yang kita harapkan ternyata tidak ada dan program berhenti dengan KeyError. Pada Chapter ini kita membedah cara membaca respons JSON secara sistematis: mengenali bentuknya, menjelajahinya, mengaksesnya dengan aman, dan menyimpannya untuk diproses kemudian.
Bentuk JSON dalam Python #
Aturan terjemahannya singkat: object JSON ({...}) menjadi dict, array JSON ([...]) menjadi list, string menjadi str, angka menjadi int atau float, dan true/false/null menjadi True/False/None. Kombinasi keduanya membentuk struktur bersarang — dictionary berisi list yang berisi dictionary lagi — persis seperti data Python yang sudah kita kenal, sehingga seluruh kemampuan indexing dan method dari Section Struktur Data Dasar langsung berlaku.
Konsekuensi praktisnya: langkah pertama membaca API baru adalah memastikan bentuk akarnya. Respons berupa [...] berarti kita mengiterasi list; respons berupa {...} berarti kita mengakses dengan key. Program yang menulis for item in respons.json() pada respons object akan gagal dengan TypeError — loop hanya berlaku untuk list.
Menjelajah Respons API Lokal #
Kita pakai server lokal yang mengirim JSON bersarang: sebuah toko berisi daftar produk, dan setiap produk punya stok yang terbagi dua lokasi. Simpan sebagai server_toko.py dan jalankan:
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
TOKO = {
"nama": "Toko Sembako Berkah",
"kota": "Bandung",
"produk": [
{
"id": 1,
"nama": "Beras Premium",
"stok": {"gudang": 40, "etalase": 5},
},
{
"id": 2,
"nama": "Minyak Goreng",
"stok": {"gudang": 25, "etalase": 8},
},
],
}
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
body = json.dumps(TOKO).encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, format, *args):
pass
ThreadingHTTPServer(("127.0.0.1", 8123), Handler).serve_forever()Klien berikut mengambil respons, menelusuri strukturnya, mengintip satu elemen, lalu menyimpan seluruh data ke file toko.json dan membacanya kembali:
import json
import requests
respons = requests.get("http://127.0.0.1:8123/", timeout=3)
data = respons.json()
print(type(data))
print(data["nama"])
for produk in data["produk"]:
total = produk["stok"]["gudang"] + produk["stok"]["etalase"]
print(f"{produk['nama']}: total stok {total}")
print(json.dumps(data["produk"][0], indent=2, ensure_ascii=False))
kupon = data.get("promosi", {}).get("kupon", "tidak ada")
print("Kupon:", kupon)
with open("toko.json", "w", encoding="utf-8") as berkas:
json.dump(data, berkas, indent=2, ensure_ascii=False)
with open("toko.json", encoding="utf-8") as berkas:
data_balik = json.load(berkas)
print("Nama toko dari file:", data_balik["nama"])Output yang Dihasilkan:
<class 'dict'>
Toko Sembako Berkah
Beras Premium: total stok 45
Minyak Goreng: total stok 33
{
"id": 1,
"nama": "Beras Premium",
"stok": {
"gudang": 40,
"etalase": 5
}
}
Kupon: tidak ada
Nama toko dari file: Toko Sembako BerkahMari kita telaah teknik pada tiap bagiannya:
- Baris 6 (
data = respons.json()): Body JSON diparse menjadidict— terlihat dari<class 'dict'>— dengan keynama,kota, danproduk. - Baris 11-13 (loop
data["produk"]): Valueprodukadalahlist, sehingga kita bisa mengiterasinya; setiap elemennyadict, danproduk["stok"]["gudang"]adalah akses dua tingkat ke dictionary di dalam dictionary. - Baris 15 (
json.dumps(..., indent=2)): Functionjson.dumps()melakukan arah sebaliknya — Python ke string JSON. Parameterindent=2merapikan menjadi multi-baris;ensure_ascii=Falsemenjaga teks non-ASCII tetap terbaca. Mencetak satu elemen seperti ini adalah cara cepat mengintip bentuk data sebelum menulis kode akses. - Baris 17 (
data.get("promosi", {})): Keypromositidak ada pada respons;.get()mengembalikan{}alih-alih menaikkanKeyError, lalu.get("kupon", "tidak ada")pada dictionary kosong menghasilkan nilai default"tidak ada". - Baris 20-24 (
json.dump/json.load): Pasangan function berkas menyimpandataketoko.jsonlalu membacanya kembali menjadi struktur Python yang sama — round trip penuh tanpa kehilangan data.
Isi file toko.json yang tercipta:
{
"nama": "Toko Sembako Berkah",
"kota": "Bandung",
"produk": [
{
"id": 1,
"nama": "Beras Premium",
"stok": {
"gudang": 40,
"etalase": 5
}
},
{
"id": 2,
"nama": "Minyak Goreng",
"stok": {
"gudang": 25,
"etalase": 8
}
}
]
}Ketika Skema Berubah #
Dua sumber ketidakpastian selalu ada pada API nyata: field optional yang kadang hilang, dan versi endpoint yang berubah. Kombinasi type() untuk memastikan bentuk, json.dumps(indent=2) untuk mengintip isi, dan rantai .get() untuk akses yang toleran merupakan tiga alat pertahanan utama kita. Tambahkan satu kebiasaan lagi: baca dokumentasi respons dengan teliti sebelum mengakses key — dokumentasi API yang baik mencantumkan mana field yang dijamin selalu ada dan mana yang bersifat optional. Pembahasan module json yang lebih dalam — termasuk custom serialization — tersedia pada Chapter Membaca dan Menulis JSON di Section File Handling.