JSON (JavaScript Object Notation) adalah bahasa pertukaran data paling umum: berkas konfigurasi aplikasi, hasil panggilan API, penyimpanan state. Format ini memetakan struktur data secara alami ke dict dan list Python, dan module json dari standard library membuat perjalanannya bolak-balik hanya satu panggilan. Keempat fungsi utamanya hanya dibedakan satu huruf — dump vs dumps, load vs loads — dan chapter ini menjelaskan kapan memakai yang mana.
dump() dan load() — Berkas #
Fungsi json.dump() menuliskan data Python ke berkas JSON, dan json.load() membacanya kembali:
import json
pengaturan = {
"nama_aplikasi": "Pencatat Belanja",
"versi": 2,
"tema": "gelap",
"fitur_aktif": ["keranjang", "diskon", "laporan"],
"batas_item": 100,
"sinkronisasi": None,
}
with open("pengaturan.json", "w", encoding="utf-8") as berkas:
json.dump(pengaturan, berkas, indent=2)
with open("pengaturan.json", encoding="utf-8") as berkas:
hasil = json.load(berkas)
print(hasil["nama_aplikasi"])
print(hasil == pengaturan)Output yang Dihasilkan:
Pencatat Belanja
TrueIsi berkas pengaturan.json:
{
"nama_aplikasi": "Pencatat Belanja",
"versi": 2,
"tema": "gelap",
"fitur_aktif": [
"keranjang",
"diskon",
"laporan"
],
"batas_item": 100,
"sinkronisasi": null
}Perhatikan pemeriksaan hasil == pengaturan yang mengembalikan True: data yang menulis-membaca-membaca kembali melingkar penuh tanpa ada yang berubah. Argument indent=2 membuat berkas terbaca manusia dengan setiap level struktur menjorok dua spasi; tanpa argument tersebut, dump() menulis semua isi dalam satu baris padat yang lebih hemat ruang. Nilai Python None dijabarkan menjadi null sesuai kosakata JSON.
dumps() dan loads() — String #
Huruf s di akhir dumps dan loads berarti string: pasangan ini bekerja pada string alih-alih berkas:
import json
teks_json = '{"nama": "Budi", "usia": 25, "aktif": true}'
data = json.loads(teks_json)
print(data, type(data).__name__)
kembali = json.dumps(data)
print(kembali)
print(json.dumps(data, indent=4, ensure_ascii=False))Output yang Dihasilkan:
{'nama': 'Budi', 'usia': 25, 'aktif': True} dict
{"nama": "Budi", "usia": 25, "aktif": true}
{
"nama": "Budi",
"usia": 25,
"aktif": true
}Mari kita bedah transformasinya:
- Baris 4 (
json.loads(teks_json)): string JSON diurai menjadi dictionary Python —trueJSON menjadiTruePython, dan25yang ditulis tanpa kutip menjadiint25. - Baris 5 (
type(data).__name__): hasil pembacaan adalahdict— tipe Python asli. - Baris 7 (
json.dumps(data)): dictionary dikembalikan menjadi string JSON;Truekembali menjaditrue.
Pola ingatannya sederhana: dump/load untuk berkas (object file), dumps/loads untuk string.
indent dan ensure_ascii #
Dua argument dumps() yang paling sering dibutuhkan sudah muncul di atas. indent menjorokkan struktur; ensure_ascii=False menjaga karakter non-ASCII tetap terbaca:
import json
data = {"menu": "Café Latte", "keterangan": "harga ±Rp28000"}
print(json.dumps(data))
print(json.dumps(data, ensure_ascii=False))Output yang Dihasilkan:
{"menu": "Caf\u00e9 Latte", "keterangan": "harga \u00b1Rp28000"}
{"menu": "Café Latte", "keterangan": "harga ±Rp28000"}Secara default (ensure_ascii=True), karakter di luar ASCII di-escape menjadi \u00e9 — sah secara JSON, tetapi menyulitkan dibaca manusia dan memperbesar teks. Dengan ensure_ascii=False, karakter ditulis apa adanya. Teks bahasa Indonesia memang mayoritas ASCII, tetapi data nyata sering memuat karakter seperti é, —, atau ± pada nama menu dan keterangan; berkas konfigurasi yang akan dibaca manusia sebaiknya selalu memakai ensure_ascii=False bersama encoding="utf-8" saat membuka berkasnya.
Tipe JSON vs Tipe Python #
Dunia JSON dan dunia Python tidak memiliki kosakata yang persis sama — tabel berikut adalah kamus lengkapnya:
| JSON | Python hasil load | Diterima dump |
|---|---|---|
| object | dict | dict |
| array | list | list, tuple (hasilnya array) |
| string | str | str |
| number | int, float | int, float |
true / false | True / False | True / False |
null | None | None |
Satu jebakan penting: JSON hanya punya array, tidak mengenal tuple. Tuple yang kita tulis menjadi array JSON, lalu terbaca kembali sebagai list:
import json
data = {
"nama": "Siti",
"tinggi_badan": 1.62,
"menikah": False,
"anak": None,
"hobi": ("memasak", "berkebun"),
}
teks = json.dumps(data, indent=2)
print(teks)
hasil = json.loads(teks)
print(type(hasil["anak"]).__name__)
print(type(hasil["hobi"]).__name__)Output yang Dihasilkan:
{
"nama": "Siti",
"tinggi_badan": 1.62,
"menikah": false,
"anak": null,
"hobi": [
"memasak",
"berkebun"
]
}
NoneType
listTuple ("memasak", "berkebun") tercetak sebagai array JSON dan kembali sebagai list. Bandingkan hasilnya dengan data asal: hasil["hobi"] == data["hobi"] bernilai False, karena list tidak pernah sama dengan tuple. Tipe lain seperti set bahkan ditolak mentah-mentah oleh dump() dengan TypeError.
JSONDecodeError — Galat Format yang Paling Sering Muncul #
Teks yang tidak mengikuti aturan JSON membuat loads()/load() melempar json.JSONDecodeError dengan pesan yang justru sangat membantu:
import json
try:
json.loads('{"nama": "Budi",}')
except json.JSONDecodeError as galat:
print("galat 1:", type(galat).__name__, "-", galat)
try:
json.loads("{'nama': 'Budi'}")
except json.JSONDecodeError as galat:
print("galat 2:", type(galat).__name__, "-", galat)
open("kosong.json", "w").close()
try:
json.load(open("kosong.json"))
except json.JSONDecodeError as galat:
print("galat 3:", type(galat).__name__, "-", galat)Output yang Dihasilkan:
galat 1: JSONDecodeError - Expecting property name enclosed in double quotes: line 1 column 17 (char 16)
galat 2: JSONDecodeError - Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
galat 3: JSONDecodeError - Expecting value: line 1 column 1 (char 0)Tiga penyebab klasik lengkap dengan pesannya: koma buntut di akhir object (galat 1), tanda kutip tunggal — JSON hanya mengenal kutip ganda (galat 2), dan berkas kosong (galat 3). Pesan galat selalu menyertakan posisi baris dan kolom persis tempat parser tersandung, jadi perbaiki teks mulai dari titik tersebut.