Panduan

JSON di Python: membaca dan menulis berkas, CSV, pathlib, dan encoding UTF-8

Diperbarui: · Skillaria.Top

Untuk bekerja dengan JSON di Python tersedia modul bawaan json: json.load membaca data dari berkas yang sudah dibuka, json.dump menulisnya ke berkas, sedangkan json.loads dan json.dumps melakukan hal yang sama pada string. Berkas dibuka dengan with open(path, encoding="utf-8"), path dibangun dengan pathlib.Path, dan tabel dibaca dengan modul csv.

Bekerja dengan JSON di Python tidak memerlukan pustaka tambahan: modul json sudah termasuk dalam pustaka standar dan mengubah dict serta list menjadi teks dan sebaliknya. Di sampingnya hampir selalu dibutuhkan tiga hal lain: fungsi open dengan konstruksi with, modul pathlib untuk path, dan modul csv untuk tabel. Berikut semuanya secara berurutan, dengan kode yang bisa dijalankan dan jebakan yang paling sering menjerat.

Fungsi open di Python: mode dan encoding

Setiap berkas teks dibuka dengan fungsi bawaan open(path, mode, encoding=...). Ia mengembalikan objek berkas yang punya metode read, readline, write, dan bisa ditelusuri baris demi baris dengan perulangan.

ModeFungsi
"r"Membaca (bawaan). Jika berkas tidak ada — FileNotFoundError
"w"Menulis. Berkas dibuat atau langsung dikosongkan saat dibuka
"a"Menambah di akhir, isi lama tidak diubah
"x"Membuat berkas baru; jika sudah ada — FileExistsError
"b"Tambahan untuk mode: bekerja dengan byte ("rb", "wb")

Untuk apa with

Berkas harus ditutup: jika tidak, data mungkin belum tertulis ke disk, dan sistem tetap menahan deskriptornya. Konstruksi with menutup berkas secara otomatis — bahkan jika di dalam blok terjadi error.

with open("notes.txt", "w", encoding="utf-8") as f:
    f.write("Baris pertama\n")
    f.write("Baris kedua\n")

with open("notes.txt", encoding="utf-8") as f:
    for line in f:
        print(line.rstrip("\n"))

Perulangan pada berkas membaca satu baris setiap kali dan tidak memuat seluruh isinya ke memori, sehingga log berukuran gigabyte pun bisa diproses. Metode f.read() mengembalikan seluruh isi sebagai satu string, dan f.readlines() mengembalikan list baris.

Cara menulis baris ke berkas tanpa menghapus isi lama

Kesalahan paling umum pemula adalah membuka berkas dengan mode "w" untuk «menambahkan» satu baris. Mode "w" mengosongkan berkas saat dibuka, sebelum write pertama. Untuk menambah di akhir, gunakan "a", dan untuk memastikan tidak menimpa berkas orang lain, gunakan "x".

with open("log.txt", "a", encoding="utf-8") as f:
    f.write("entri baru\n")

Perhatikan bahwa write tidak menambahkan pindah baris sendiri — "\n" harus ditulis secara eksplisit. Anda juga bisa memakai print(..., file=f), yang otomatis menambahkan pindah baris.

Encoding UTF-8: mengapa muncul karakter aneh

Sebelum Python 3.15, open() tanpa parameter encoding memakai encoding sistem. Di Linux dan macOS itu UTF-8, sedangkan di Windows dengan locale Rusia adalah cp1251. Skrip yang berjalan baik di server bisa menampilkan «Р•РґР°» alih-alih «Еда» di laptop rekan, atau berakhir dengan UnicodeDecodeError.

Di Python 3.15, menurut PEP 686, mode UTF-8 aktif secara bawaan: open() tanpa encoding membaca dan menulis UTF-8 di semua OS. Perilaku lama bisa dikembalikan dengan variabel lingkungan PYTHONUTF8=0 atau flag -X utf8=0, dan encoding sistem bisa diminta secara eksplisit dengan encoding="locale".

import sys
print(sys.flags.utf8_mode)   # 1 di Python 3.15

open("legacy.txt", encoding="locale")   # encoding sistem
open("old_excel.csv", encoding="cp1251")  # berkas yang disimpan dalam cp1251

Sisi lainnya: CSV lama dari Excel atau skrip Windows yang disimpan dalam cp1251 sekarang akan dibaca sebagai UTF-8 dan memunculkan UnicodeDecodeError. Solusinya: baca dengan encoding="cp1251", lalu simpan ulang dalam UTF-8. Kebiasaan baik tetap sama — tulis encoding secara eksplisit, sehingga kode berperilaku sama di versi Python apa pun.

pathlib di Python: path sebagai objek

Menggabungkan path dengan string ("data" + "/" + name) merepotkan dan bisa rusak di OS yang berbeda. Modul pathlib menyediakan kelas Path, tempat path digabung dengan operator /, dan objeknya punya properti yang jelas.

from pathlib import Path

data = Path("data")
csv_path = data / "expenses.csv"

print(csv_path.name)    # expenses.csv
print(csv_path.stem)    # expenses
print(csv_path.suffix)  # .csv
print(csv_path.parent)  # data
print(csv_path.exists())

data.mkdir(parents=True, exist_ok=True)
(data / "hello.txt").write_text("Halo", encoding="utf-8")
print((data / "hello.txt").read_text(encoding="utf-8"))

for p in data.glob("*.csv"):
    print(p)

Metode read_text dan write_text menggantikan kombinasi open + with untuk berkas kecil. Objek Path bisa diberikan ke open, json, maupun csv — semuanya menerimanya. Path yang relatif terhadap skrip itu sendiri, bukan folder kerja, didapat dengan Path(__file__).parent / "data".

Di Python 3.14, Path mendapat metode copy() dan move(), sehingga untuk penyalinan sederhana shutil tidak lagi diperlukan. Di versi yang sama, pustaka standar juga mendapat modul compression.zstd untuk kompresi Zstandard (PEP 784).

JSON di Python: load, dump, loads, dumps

JSON adalah format teks untuk pertukaran data: API mengirimnya, dan konfigurasi serta laporan sering disimpan dalam bentuk ini. Modul json melakukan dua operasi: serialisasi (objek Python → teks JSON) dan deserialisasi (teks → objek). Ada empat fungsi, dan kebingungan antara keempatnya adalah pertanyaan yang sering muncul.

FungsiApa yang dilakukan
json.dumps(obj)Objek → string JSON
json.loads(s)String JSON → objek
json.dump(obj, f)Objek → ditulis ke berkas yang sudah dibuka
json.load(f)Membaca JSON dari berkas yang sudah dibuka

Cara mengingatnya: huruf s di akhir berarti string — bekerja dengan string; tanpa s — bekerja dengan berkas.

Kesesuaian tipe

PythonJSON
dictobjek {}
list, tuplearray []
strstring
int, floatangka
True / Falsetrue / false
Nonenull

Tuple akan menjadi list saat dibaca kembali. Set, tanggal, dan Decimal tidak dikenali oleh modul ini dan akan menghasilkan TypeError: Object of type ... is not JSON serializable.

Cara menyimpan JSON ke berkas

import json
from pathlib import Path

report = {
    "month": "2026-10",
    "total": 15230.5,
    "categories": {"makan": 8200, "transportasi": 2100},
    "tags": ["rumah", "kerja"],
}

path = Path("data/report.json")
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", encoding="utf-8") as f:
    json.dump(report, f, ensure_ascii=False, indent=2)

Parameter ensure_ascii=False menyimpan huruf non-ASCII apa adanya. Tanpa itu, «makan» di berkas akan berupa escape \u — itu JSON yang valid, tetapi tidak nyaman dibaca manusia. indent=2 membuat format rapi dengan indentasi.

Cara membaca JSON dari berkas

with open("data/report.json", encoding="utf-8") as f:
    data = json.load(f)

print(data["total"])               # 15230.5
print(data["categories"]["makan"]) # 8200

Hasilnya adalah dict atau list biasa, dan Anda bisa bekerja dengannya seperti biasa. Jika berkas bukan JSON atau terpotong, akan muncul exception json.JSONDecodeError dengan nomor baris dan posisi — sebaiknya ditangkap ketika data datang dari luar.

Parsing string JSON dan tampilan rapi

import json

raw = '{"name": "Rina", "skills": ["python", "sql"], "active": true}'
user = json.loads(raw)
print(user["skills"][0])   # python

print(json.dumps(user, ensure_ascii=False, indent=2, sort_keys=True))

sort_keys=True mengurutkan kunci saat ditampilkan — berguna agar perbedaan berkas mudah dibandingkan di git. Untuk merapikan berkas dari terminal ada utilitas bawaan: python -m json.tool data/report.json.

Respons HTTP API juga berupa string JSON. Pustaka requests menguraikannya dengan metode response.json(), dan di dalamnya itu tetap json.loads.

Jebakan: kunci JSON selalu string

d = {1: "januari", 2: "februari"}
back = json.loads(json.dumps(d))
print(back == d)   # False
print(back)        # {'1': 'januari', '2': 'februari'}
# back[1] -> KeyError, harus back["1"]

fixed = {int(k): v for k, v in back.items()}

Dalam JSON, kunci objek hanya boleh berupa string, sehingga kunci angka berubah menjadi string setelah melewati serialisasi. Kembalikan tipenya saat membaca.

Tipe sendiri: tanggal dan Decimal

import json
from datetime import date

def default(obj):
    if isinstance(obj, date):
        return obj.isoformat()
    raise TypeError(f"tidak bisa menserialisasi {type(obj).__name__}")

print(json.dumps({"day": date(2026, 10, 1)}, default=default))
# {"day": "2026-10-01"}

Fungsi di parameter default dipanggil untuk setiap objek yang tidak dikenali. Untuk kasus yang lebih rumit, Anda bisa mewarisi json.JSONEncoder dan menimpa metode default.

Cara menambah elemen ke array JSON

JSON tidak bisa ditambahi di akhir seperti log teks: berkas harus dibaca, diubah, lalu ditulis ulang secara utuh.

path = Path("data/items.json")
items = json.loads(path.read_text(encoding="utf-8")) if path.exists() else []
items.append({"id": len(items) + 1, "title": "baru"})
path.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")

Jika datanya banyak dan terus bertambah, lebih nyaman memakai format JSON Lines: satu objek JSON per baris, dan berkasnya dibuka dengan mode "a".

CSV di Python: modul csv

CSV adalah tabel dalam berkas teks: baris adalah catatan, nilainya dipisahkan koma. Modul csv menangani tanda kutip, koma di dalam nilai, dan pindah baris dengan benar — hal yang tidak bisa dijamin oleh split(",") buatan sendiri.

import csv

with open("data/expenses.csv", newline="", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    total = 0.0
    for row in reader:
        total += float(row["amount"])
        print(row["date"], row["category"], row["amount"])
print("Total:", total)

csv.DictReader mengubah setiap baris menjadi dict dengan nama kolom dari header berkas. Semua nilainya berupa string: angka dan tanggal harus diubah sendiri (float, int, date.fromisoformat). Berkas dengan pemisah «;» (format yang dihasilkan Excel versi Rusia) dibaca dengan parameter delimiter=";".

rows = [
    {"date": "2026-10-01", "category": "makan", "amount": 450},
    {"date": "2026-10-02", "category": "transportasi", "amount": 60},
]
with open("data/out.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["date", "category", "amount"])
    writer.writeheader()
    writer.writerows(rows)

Saat menulis, newline="" wajib: tanpanya di Windows akan muncul baris kosong di antara baris data. Jika berkas akan dibuka di Excel, gunakan encoding="utf-8-sig" — penanda BOM membantu Excel mengenali UTF-8.

Dari CSV ke JSON: contoh lengkap

import csv
import json
from collections import defaultdict
from pathlib import Path

src = Path("data/expenses.csv")
dst = Path("data/report.json")

by_category = defaultdict(float)
with src.open(newline="", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        by_category[row["category"]] += float(row["amount"])

report = {"total": sum(by_category.values()), "categories": dict(by_category)}
dst.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
print(dst.read_text(encoding="utf-8"))

Ini inti dari setiap skrip laporan: masuk dari CSV, agregasi di dalam dict, keluar ke JSON.

Kesalahan umum

  • FileNotFoundError — path relatif dihitung dari folder tempat program dijalankan, bukan dari skrip. Gunakan Path(__file__).parent.
  • Isi berkas hilang — berkas dibuka dengan "w" padahal seharusnya "a".
  • UnicodeDecodeError — berkas memakai encoding lain; tentukan secara eksplisit (cp1251, utf-8-sig).
  • TypeError: Object of type set is not JSON serializable — ubah ke list, atau berikan default.
  • KeyError setelah memuat JSON — kunci angka sudah menjadi string.
  • Berkas sendiri dinamai json.py atau csv.py — ia menutupi modul standar, dan import json akan mengimpor berkas itu.

Ringkasan

Untuk JSON di Python cukup pustaka standar: json.load/json.dump untuk berkas, loads/dumps untuk string, dengan ensure_ascii=False dan indent untuk keluaran yang mudah dibaca. Buka berkas dengan with dan encoding yang eksplisit, bangun path dengan pathlib, dan baca tabel dengan csv.DictReader. Topik ini dipraktikkan dalam kursus Python: pencatat pengeluaran membaca data dari CSV dan menyimpan laporan ke JSON.

Pertanyaan umum

Apa bedanya json.load dan json.loads?

json.load membaca JSON dari objek berkas yang sudah dibuka, sedangkan json.loads mengurai string. Demikian juga json.dump menulis ke berkas, dan json.dumps mengembalikan string.

Bagaimana menyimpan huruf non-ASCII di JSON tanpa escape \u?

Berikan ensure_ascii=False ke json.dump atau json.dumps, dan buka berkasnya dengan encoding="utf-8".

Perlukah memasang pustaka khusus untuk JSON di Python?

Tidak. Modul json sudah ada di pustaka standar, cukup tulis import json.

Bisakah JSON menyimpan tanggal atau set?

Tidak langsung. Keduanya harus diubah dulu: tanggal menjadi string ISO dengan isoformat, set menjadi list; atau berikan fungsi ke parameter default.

Bagaimana membaca JSON yang terlalu besar untuk memori?

json.load membaca seluruh berkas sekaligus. Untuk data besar, simpan dalam format JSON Lines dan urai baris demi baris dengan json.loads.

Pelajari topik ini lewat video

Topik ini dibahas mendalam dengan praktik di kursus “Python 3.15: от новичка до профи”.

Ke kursus