JSON Toolkit

JSONL dalam praktik: membuka, mengonversi, dan memperbaiki baris rusak

Intinya: Memberikan seluruh file .jsonl ke JSON.parse lalu menerima error hampir tidak pernah berarti file rusak — artinya strategi membaca yang salah. Panduan ini mencakup cara standar memeriksa dan membaca JSONL baris demi baris, perintah konversi dua arah dengan array JSON, tabel enam jenis baris rusak, dan cara benar mengonsumsi NDJSON dari respons HTTP streaming.

Menengah±9 menit bacaDiterbitkan 2026-11-06Diperbarui 2026-11-06
Panduan praktis membaca file JSONL baris demi baris dan mengisolasi baris rusak

Intinya: setiap baris file JSONL adalah nilai JSON yang valid dan mandiri, tetapi keseluruhan file bukan satu dokumen JSON. Jika memberikan seluruh file ke JSON.parse atau json.load gagal, sembilan dari sepuluh kali file-nya baik-baik saja — strategi membaca Anda yang keliru.

Satu rekaman per baris, tanpa koma di antara baris, tanpa array pembungkus — JSONL melepas "struktur keseluruhan dokumen" untuk mendapatkan penulisan tambahan, pembacaan streaming, dan isolasi kerusakan per baris. Ketiga keunggulan itu hanya terwujud ketika file diproses baris demi baris, dan ketiganya berubah menjadi error begitu Anda mencoba mengurai seluruh file sekaligus.

1. Tiga nama, satu format: JSONL, NDJSON, JSON Lines

Pemula sering menyangka perbedaan nama berarti perbedaan sintaks. Tidak:

Nama Konteks khas Ekstensi / media type
JSON Lines Rekayasa data, dataset ML (pandas, Spark, Hugging Face) .jsonl
NDJSON API HTTP streaming, data real-time .ndjson, application/x-ndjson
JSONL Singkatan sehari-hari untuk salah satunya —

Alat apa pun yang membaca salah satu pasti membaca yang lain. Satu-satunya kriteria memilih nama adalah konvensi ekosistem hilir Anda: tulis .jsonl saat menyiapkan dataset untuk Hugging Face, atur application/x-ndjson saat menyajikan API streaming. File JSONL yang portabel memenuhi tiga aturan: pengodean UTF-8 (tanpa BOM), satu nilai JSON lengkap per baris, baris dipisahkan \n. Baris baru penutup di akhir file sangat disarankan, dan baris kosong bukan rekaman yang valid. Detail protokol dan alasan desain dibahas dalam ringkasan JSON Lines dan NDJSON kami; artikel ini secara ketat membahas apa yang harus dilakukan ketika file sudah ada di tangan.

2. Pemeriksaan: head dulu, lalu grep, editor paling akhir

Mengklik dua kali file .jsonl berukuran 500 MB untuk membukanya di editor adalah kesalahan paling umum: tiga juta baris teks plus penyorotan sintaks plus buffer undo akan membekukan hampir semua editor — dan JSON tidak bersalah. Mengurai dokumen 45 MB memakan sekitar 208 ms di Node 22; yang membeku adalah perenderan. Format berorientasi baris diciptakan untuk alat berbasis baris, karena rekaman berimpit dengan baris fisik:

# Ukur skalanya dulu: jumlah rekaman dan ukuran file, tanpa membaca isi
wc -l events.jsonl && du -h events.jsonl

# Lihat wujud satu rekaman (pastikan field dan strukturnya)
head -n 1 events.jsonl | jq .

# Temukan rekaman target langsung: alat berbasis baris berfungsi begitu saja — hak istimewa JSONL
grep '"level":"error"' events.jsonl | head -n 5
tail -n 100 events.jsonl | jq 'select(.latency_ms > 500)'

Perhatikan perintah terakhir: setiap baris keluar dari tail tetap nilai JSON mandiri, sehingga bisa langsung masuk ke jq tanpa perlu membangun ulang array. Pendekatan ini bekerja identik pada file beberapa gigabyte dengan overhead memori nyaris nol — prinsip yang sama dengan mengapa file JSON besar memperlambat halaman.

3. Tiga pola standar pembacaan baris demi baris

Pembacaan baris demi baris harus menangani tiga hal: melewati baris kosong (terutama baris baru penutup), melaporkan error dengan nomor baris, dan tidak membiarkan satu baris rusak membunuh seluruh pekerjaan. Pola standar di ketiga bahasa berputar pada tiga poin ini.

Python, hanya pustaka standar:

import json

with open("events.jsonl", encoding="utf-8") as f:
    for line_number, line in enumerate(f, start=1):
        if not line.strip():          # lewati baris kosong
            continue
        try:
            record = json.loads(line) # urai setiap baris secara mandiri
        except json.JSONDecodeError as e:
            print(f"Baris {line_number} gagal: {e}")  # laporkan, jangan berhenti
            continue                  # isolasi baris rusak
        handle(record)

Node dengan modul readline bawaan, memori konstan berapa pun ukuran file:

import fs from "node:fs";
import readline from "node:readline";

const input = fs.createReadStream("events.jsonl", "utf8");
const lines = readline.createInterface({ input, crlfDelay: Infinity });
let lineNumber = 0;
for await (const line of lines) {
  lineNumber += 1;
  if (!line.trim()) continue;
  try {
    handle(JSON.parse(line));
  } catch (e) {
    console.error(`Baris ${lineNumber}: ${e.message}`);
  }
}

Di Go 1.27, encoding/json/v2 menjadi standar bawaan, dekoder streaming per nilai-nya pas sekali dengan JSONL; gunakan gojq jika perlu menanamkan ekspresi jq. Soal pustaka, jangan buru-buru memasang paket: jsonlines di Python melaporkan nomor dan teks mentah setiap baris yang gagal, dan orjsonl membaca serta menulis file terkompresi gzip/bzip2/zstd langsung; paket Node ndjson tidak merilis versi sejak 2020 — readline bawaan sudah cukup.

4. Konversi dua arah dengan array JSON

Arah itu penting; terbalikkan dan semua perintah akan salah. Ingat satu aturan: array → JSONL adalah map (setiap rekaman dipadatkan jadi satu baris), JSONL → array adalah collect (baris dikumpulkan menjadi satu daftar).

# Array JSON → JSONL: -c memadatkan setiap elemen jadi satu baris
jq -c '.[]' records.json > records.jsonl

# JSONL → array JSON: -s mengumpulkan seluruh input menjadi satu array
jq -s '.' events.jsonl > records.json

# Menyaring JSONL baris demi baris: -s tidak perlu, jq memproses input sebagai stream
jq -c 'select(.active == true)' events.jsonl > active.jsonl

Satu baris perintah Python setara: array → JSONL menulis json.dumps(r, ensure_ascii=False) + "\n" per rekaman; arah sebaliknya mengumpulkan hasil json.loads baris demi baris ke daftar. Masing-masing arah punya satu jebakan. Arah pergi (array → JSONL), setiap baris baru asli di dalam rekaman harus di-escape oleh json.dumps — kalau tidak satu rekaman akan terkoyak menjadi beberapa baris. Arah balik (JSONL → array), jangan menyambung baris-baris menjadi satu string raksasa lalu mengurainya — dengan jutaan baris itu satu salinan penuh yang tidak perlu di memori. Agregasi (jumlah, pengelompokan, rata-rata) butuh -s yang memuat semuanya; pada file beberapa GB, beralihlah ke awk atau akumulator streaming di Python.

5. Enam jenis baris rusak dan cara mendiagnosisnya

Hampir semua error JSONL berada di level baris. Validasi baris demi baris dengan pelaporan nomor baris adalah satu-satunya diagnosa andal. Diurutkan berdasarkan frekuensi:

Gejala Akar penyebab Perbaikan
Extra data di akhir baris Koma ditambahkan di antara baris Hapus; setiap baris harus berdiri sendiri
Satu rekaman terbelah menyeberangi baris Baris baru asli tak ter-escape di dalam rekaman (diformat manual) Padatkan ulang dengan json.dumps / jq -c
Baris pertama gagal dengan Unexpected token ï File mengandung BOM Simpan ulang sebagai UTF-8 tanpa BOM
Hanya baris terakhir beralat, padahal tampak benar Baris baru penutup hilang atau ada spasi liar Tambahkan \n; lewati baris kosong sesuai konvensi
Error baris baru di dalam string Baris baru literal di dalam string, bukan \n Perbaiki generator; jangan pernah menyambung string JSON secara manual
Bilangan bulat besar berubah nilai Diurai lewat double (terpotong di 2^53) Gunakan parser yang memelihara presisi, lihat presisi bigint

Rutinitas diagnosa selalu tiga langkah: head -n 1 untuk memastikan pengodean dan struktur baris pertama; jalankan skrip baris demi baris yang melaporkan nomor setiap baris rusak alih-alih berhenti di yang pertama; lalu keluarkan baris rusak satu per satu — satu baris adalah satu rekaman penuh, memeriksanya secara terpisah tidak menelan biaya apa pun. Untuk impor massal, kebijakan yang disarankan adalah "karantina, bukan menghentikan": tulis baris rusak beserta nomornya ke errors.jsonl, terus muatkan rekaman yang baik, lalu tentukan keputusan impor ulang berdasarkan proporsi baris rusak.

6. JSONL di atas HTTP: konsumsi baris demi baris dan backpressure

Pola khas NDJSON di atas HTTP: server menulis terus menerus, klien mengonsumsi terus menerus. Di sisi klien, jangan panggil response.json() — ia menyangga seluruh body sebelum penguraian, menghapus makna streaming. Baca stream body secara langsung, belah pada \n, dan bawa fragmen ekor yang belum lengkap ke iterasi berikutnya:

const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
let buffer = "";
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += value;
  const lines = buffer.split("\n");
  buffer = lines.pop();               // potongan terakhir bisa jadi baris yang belum lengkap
  for (const line of lines) {
    if (line.trim()) handle(JSON.parse(line));
  }
}

Di sisi server dua hal wajib: pertama, saat res.write() mengembalikan false, tunggu event drain (backpressure), kalau tidak memori tumbuh tanpa batas setiap kali konsumen lebih lambat daripada produsen; kedua, matikan middleware gzip pada rute streaming — gzip harus menyangga seluruh respons sebelum bisa memampatkan apa pun, dan itu melumpuhkan streaming NDJSON sepenuhnya. Kedua perilaku dikonfirmasi lewat uji lokal dan beberapa sumber independen berbahasa Inggris, bukan dugaan.

7. Kapan JSONL adalah pilihan yang keliru

Ruang negatif pemilihan format juga penting. Tiga skenario sebaiknya tetap memakai array JSON:

Kebutuhan Mengapa JSONL kurang cocok Gunakan sebagai gantinya
Memvalidasi struktur seluruh dokumen Tidak ada struktur keseluruhan; penyimpangan field luput terlihat Array JSON + Schema
Akses acak ke rekaman ke-N Panjang baris berubah-ubah; pencarian biner tak bisa SQLite / Parquet
Penyarangan dalam, referensi antar rekaman Setiap baris terisolasi; referensi tak punya tempat tinggal Dokumen JSON standar

Aturan saku satu kalimat: jika rekaman datang seiring waktu atau dikonsumsi baris demi baris (log, kejadian, ekspor, set pelatihan), gunakan JSONL; jika volume kecil dibuat sekali, dikonsumsi sekali, dan butuh semantik dokumen utuh, gunakan array JSON. Varian turunan (JSON Text Sequences dengan pemisah RS) adalah topik lain — lihat artikel RFC 7464.

FAQ

Apakah ada perbedaan nyata antara JSONL dan NDJSON?

Secara rekayasa, tidak. Keduanya berarti "satu nilai JSON lengkap per baris, tanpa pembungkus luar, tanpa koma di antara baris", dan alat yang mendukung satu pasti mendukung yang lain. Perbedaannya pada penamaan dan kebiasaan ekosistem: .jsonl mendominasi rekayasa data dan dataset ML; .ndjson dan application/x-ndjson mendominasi API HTTP streaming. Saat berintegrasi dengan sistem eksternal, ikuti ekstensi dan media type yang ditentukan dalam dokumentasinya.

Bisakah file .jsonl dibaca langsung dengan JSON.parse?

Bukan seluruh file. JSON.parse menerima tepat satu teks JSON, sedangkan JSONL adalah banyak nilai mandiri yang disusun baris demi baris, jadi penguraian seluruh file selalu gagal dengan "Unexpected token" atau "Extra data". Pecah menjadi baris dan urai setiap baris tak kosong secara terpisah, laporkan nomor baris bila gagal.

Jika satu baris file JSONL rusak, apakah seluruh file hancur?

Tidak — isolasi kerusakan per baris adalah inti desain format ini. Baris rusak hanya merugikan dirinya sendiri: lewati, catat nomornya, dan lanjutkan membaca. Untuk impor massal, karantina baris rusak ke file terpisah, muat rekaman yang baik seperti biasa, lalu tentukan perbaikan dan impor ulang berdasarkan proporsi baris rusak.

Bagaimana mengonversi JSONL ke Excel atau CSV?

Pertama ubah menjadi array atau ekstrak field baris demi baris, lalu jalankan konversi tabel: padatkan dengan jq -c '.[]' data.jsonl dan serahkan ke konverter, atau alirkan setiap baris lewat json.loads ke pandas. Hati-hati: field bersarang yang diratakan menghasilkan nama kolom bertitik, field array hanya bisa diserialisasi sebagai teks di dalam sel, dan kolom ID panjang sebaiknya dinyatakan sebagai string sejak awal untuk menghindari kehilangan presisi.

Referensi

Alat yang sesuai

Artikel terkait

JSON Lines dan NDJSON: Data Streaming

Satu nilai JSON per baris memberi Anda kemampuan menambahkan, streaming, dan kerusakan yang terisolasi pada satu baris; harganya adalah tidak adanya struktur keseluruhan, sehingga validasi harus dilakukan baris demi baris.

Menengah±10 menit bacaDiperbarui 2026-10-30

Parsing, Rendering, dan Streaming JSON Besar

Parsing sering bukan hambatan; rendering dan penyalinanlah yang menjadi hambatan. Optimalkan sepanjang empat lapisan "parse → traverse → render → transport" agar usaha Anda tidak sia-sia.

Menengah±13 menit bacaDiperbarui 2026-10-31