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:

python
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:

text
Pencatat Belanja
True

Isi berkas pengaturan.json:

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:

python
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:

text
{'nama': 'Budi', 'usia': 25, 'aktif': True} dict
{"nama": "Budi", "usia": 25, "aktif": true}
{
    "nama": "Budi",
    "usia": 25,
    "aktif": true
}

Mari kita bedah transformasinya:

  1. Baris 4 (json.loads(teks_json)): string JSON diurai menjadi dictionary Python — true JSON menjadi True Python, dan 25 yang ditulis tanpa kutip menjadi int 25.
  2. Baris 5 (type(data).__name__): hasil pembacaan adalah dict — tipe Python asli.
  3. Baris 7 (json.dumps(data)): dictionary dikembalikan menjadi string JSON; True kembali menjadi true.

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:

python
import json

data = {"menu": "Café Latte", "keterangan": "harga ±Rp28000"}

print(json.dumps(data))
print(json.dumps(data, ensure_ascii=False))

Output yang Dihasilkan:

text
{"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:

JSONPython hasil loadDiterima dump
objectdictdict
arraylistlist, tuple (hasilnya array)
stringstrstr
numberint, floatint, float
true / falseTrue / FalseTrue / False
nullNoneNone

Satu jebakan penting: JSON hanya punya array, tidak mengenal tuple. Tuple yang kita tulis menjadi array JSON, lalu terbaca kembali sebagai list:

python
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:

text
{
  "nama": "Siti",
  "tinggi_badan": 1.62,
  "menikah": false,
  "anak": null,
  "hobi": [
    "memasak",
    "berkebun"
  ]
}
NoneType
list

Tuple ("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:

python
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:

text
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.