Aplikasi kasir mencatat transaksi baru setiap menit, dan file pencatatan terus membesar sepanjang hari. Jika seluruh transaksi disimpan sebagai satu array JSON, dokumen tersebut baru sah setelah kurung siku penutup ditulis — menambah satu transaksi berarti membaca seluruh file, menambah elemen, lalu menulis ulang. JSON Lines (JSONL) menjawab masalah ini dengan satu aturan sederhana: setiap baris berisi tepat satu objek JSON yang lengkap dan berdiri sendiri. Format ini menjadi standar de facto untuk log aplikasi, streaming, dan export data bertahap — BigQuery memakainya untuk mengekspor tabel besar — seperti dijelaskan situs resminya di jsonlines.org .

Menulis JSONL tidak membutuhkan library khusus — kita tetap memakai json.dumps(), hanya dengan dua disiplin tambahan: hasil serialisasi harus satu baris (tanpa indent), dan setiap baris diakhiri karakter baris baru. Data transaksi toko dari chapter sebelumnya kita tulis ulang dalam format ini:

python
import json

transaksi = [
    {"id": 1, "kasir": "Budi", "produk": "Kopi Robusta", "qty": 2, "harga": 45000},
    {"id": 2, "kasir": "Siti", "produk": "Teh Melati", "qty": 3, "harga": 15000},
    {"id": 3, "kasir": "Budi", "produk": "Gula Aren, 500g", "qty": 1, "harga": 25000},
    {"id": 4, "kasir": "Dewi", "produk": "Susu UHT 1L", "qty": 2, "harga": 18000},
]

with open("transaksi.jsonl", "w", encoding="utf-8") as berkas:
    for t in transaksi:
        berkas.write(json.dumps(t, ensure_ascii=False) + "\n")

print(open("transaksi.jsonl", encoding="utf-8").read())

Output yang Dihasilkan:

text
{"id": 1, "kasir": "Budi", "produk": "Kopi Robusta", "qty": 2, "harga": 45000}
{"id": 2, "kasir": "Siti", "produk": "Teh Melati", "qty": 3, "harga": 15000}
{"id": 3, "kasir": "Budi", "produk": "Gula Aren, 500g", "qty": 1, "harga": 25000}
{"id": 4, "kasir": "Dewi", "produk": "Susu UHT 1L", "qty": 2, "harga": 18000}

Mari kita telaah baris-baris kode di atas:

  1. Baris 10-12 (for t in transaksi: ...): setiap dictionary diserialisasi menjadi satu string JSON padat, lalu ditulis dengan write() dan ditutup "\n" — satu record, satu baris.
  2. Baris 12 (json.dumps(t, ensure_ascii=False)): tanpa indent hasilnya pasti satu baris; karakter é dan koma di dalam nama produk tetap aman karena JSONL dibaca per baris, bukan per delimiter.
  3. Baris 14 (print(open(...).read())): menampilkan isi file — empat baris, masing-masing adalah dokumen JSON yang valid jika dites sendiri-sendiri.

Membaca Baris demi Baris #

Iterasi atas objek file di Python membaca isi secara lazy: satu baris diproses pada satu waktu, dan sisa file tidak pernah tersentuh. Perilaku ini membuat JSONL cocok untuk file berukuran besar — proses berhenti kapan pun hasil yang dibutuhkan sudah diperoleh:

python
import json

with open("transaksi.jsonl", encoding="utf-8") as berkas:
    for nomor, baris in enumerate(berkas, start=1):
        t = json.loads(baris)
        print(f"baris {nomor}: transaksi {t['id']} oleh {t['kasir']}")
        if t["id"] == 2:
            print("berhenti di sini, sisa file tidak diproses")
            break

teks_utuh = open("transaksi.jsonl", encoding="utf-8").read()
try:
    json.loads(teks_utuh)
except json.JSONDecodeError as galat:
    print("JSONDecodeError:", galat)

Output yang Dihasilkan:

text
baris 1: transaksi 1 oleh Budi
baris 2: transaksi 2 oleh Siti
berhenti di sini, sisa file tidak diproses
JSONDecodeError: Extra data: line 2 column 1 (char 79)

Tiga bukti penting ada pada hasil di atas. Pertama, break menghentikan pemrosesan setelah transaksi kedua — baris ketiga dan keempat tidak pernah dibaca. Kedua, json.loads(baris) dipanggil per baris, sehingga kegagalan satu baris tidak menular ke baris lain. Ketiga, percobaan mem-parse seluruh isi file sebagai satu dokumen JSON memicu JSONDecodeError — JSONL memang bukan satu dokumen JSON, melainkan rangkaian dokumen.

Menambah Data dengan Mode Append #

Kekuatan sesungguhnya JSONL muncul saat data bertambah. Mode "a" pada open() menulis langsung di akhir file tanpa membaca isi lama, jadi menambah satu transaksi tidak peduli seberapa besar file pencatatan tersebut:

python
import json

transaksi_baru = {"id": 5, "kasir": "Siti", "produk": "Café Latte", "qty": 1, "harga": 32000}

with open("transaksi.jsonl", "a", encoding="utf-8") as berkas:
    berkas.write(json.dumps(transaksi_baru, ensure_ascii=False) + "\n")

with open("transaksi.jsonl", encoding="utf-8") as berkas:
    jumlah = sum(1 for baris in berkas if baris.strip())
print("jumlah baris sekarang:", jumlah)

with open("transaksi.jsonl", encoding="utf-8") as berkas:
    for baris in berkas:
        t = json.loads(baris)
        if t["id"] == 5:
            print("transaksi terakhir:", t["produk"], t["harga"])

Output yang Dihasilkan:

text
jumlah baris sekarang: 5
transaksi terakhir: Café Latte 32000

Perbandingan lengkap kedua gaya penyimpanan dirangkum berikut:

AspekArray JSON (.json)JSON Lines (.jsonl)
Struktursatu dokumen array utuhsatu objek JSON per baris
Menambah databaca semua, tulis ulang filetambah satu baris di akhir
Proses sebagian datawajib parse dokumen penuhberhenti kapan saja per baris
Satu baris rusakseluruh dokumen gagal parsehanya baris tersebut gagal

JSONL menuntut satu objek per baris, tetapi banyak sistem lama justru menyimpan dokumen bertingkat dengan atribut dan namespace — dunia XML.