JSON5 dan JSONC: JSON dengan Komentar
Intinya: Kedua dialek ada karena konfigurasi yang ditulis manusia membutuhkan komentar dan koma di akhir, tetapi sintaks dan jangkauan ekosistemnya berbeda, sehingga pilihan yang salah membuat toolchain Anda macet.

Intinya: gunakan JSONC untuk komentar di dalam konfigurasi VS Code dan TypeScript, gunakan JSON5 ketika Anda membutuhkan sintaks literal ES5.1 yang lebih luas seperti tanda kutip tunggal atau kunci tanpa tanda kutip, dan pertahankan API publik pada JSON standar.
JSON standar tidak mengizinkan komentar, sehingga setiap tim diam-diam tetap menulisnya — sampai suatu hari build rusak. Alih-alih melakukannya secara sembunyi-sembunyi, pilihlah dialek yang tepat.
1. Kedua dialek memiliki motivasi berbeda
- JSONC (JSON with Comments): JSON standar plus komentar dan koma di akhir, hampir tanpa perubahan sintaks lain. Ia adalah standar de facto ekosistem VS Code —
settings.json,tsconfig.json, dan.eslintrc.jsonsemuanya menggunakannya. - JSON5: jauh lebih ambisius, selaras langsung dengan object literal ES5.1 — tanda kutip tunggal, kunci tanpa tanda kutip, heksadesimal,
Infinity/NaN, kelanjutan baris dengan backslash, dan tanda+di depan.
Satu kalimat untuk membedakan keduanya: JSONC hanya membuat penulisan nyaman; JSON5 memungkinkan Anda menulis file konfigurasi seolah-olah itu JavaScript.
2. Tabel perbandingan sintaks
| Fitur | JSON standar | JSONC | JSON5 |
|---|---|---|---|
Komentar // /* */ |
✗ | ✓ | ✓ |
| Koma di akhir | ✗ | ✓ | ✓ |
| String dengan tanda kutip tunggal | ✗ | ✗ | ✓ |
| Kunci tanpa tanda kutip | ✗ | ✗ | ✓ |
| Angka heksadesimal | ✗ | ✗ | ✓ |
Titik desimal di depan/di belakang .5 5. |
✗ | ✗ | ✓ |
Angka yang dimulai dengan + |
✗ | ✗ | ✓ |
Infinity / NaN |
✗ | ✗ | ✓ |
Kelanjutan \ di akhir baris |
✗ | ✗ | ✓ |
| String multi-baris | ✗ | ✗ | ✓ (diimplementasikan melalui kelanjutan) |
| Nilai tingkat atas apa pun | ✓ | ✓ | ✓ |
Spesifikasi JSON5 lengkap tidak lebih dari satu halaman; paket json5 telah lama dipelihara oleh komunitas, dan peramban serta Node berperilaku identik.
3. Parsing: dua API, dua jenis kemampuan pelokasian
3.1 JSON5: parsing seluruh dokumen yang sederhana
import JSON5 from 'json5';
const config = JSON5.parse(`
{
// service port
port: 8080,
host: '0.0.0.0',
features: ['json5', 'jsonc',],
retries: 0x10,
}
`);
console.log(config.port, config.retries); // 8080 16
JSON5.parse mengembalikan objek biasa tanpa informasi offset — karena ia tidak diarahkan ke skenario pengeditan, hanya ke "membaca konfigurasi".
3.2 JSONC: pohon sintaks dengan offset
Nilai jsonc-parser bukan pada parsing, melainkan pada pengeditan. Ia menawarkan tiga lapisan API:
| API | Tujuan | Penggunaan tipikal |
|---|---|---|
parse |
Mem-parse menjadi pohon yang membawa offset/length |
Penyorotan sintaks, folding, navigasi |
visit |
Penelusuran depth-first; callback menerima path node | Inspeksi mirip JSONPath |
modify + applyEdits |
Menghasilkan edit teks minimal, lalu menerapkannya ke teks asli | Mengubah nilai sambil mempertahankan komentar |
import { parseTree, findNodeAtLocation, modify, applyEdits } from 'jsonc-parser';
const text = `{
// port configuration
"server": {
"port": 8080 // 80 in production
}
}`;
const tree = parseTree(text);
const portNode = findNodeAtLocation(tree!, ['server', 'port']);
console.log(portNode?.offset, portNode?.length);
const edits = modify(text, ['server', 'port'], 3000, {
formattingOptions: { insertSpaces: true, tabSize: 2 },
});
console.log(applyEdits(text, edits));
Output mempertahankan komentar asli dan hanya mengganti tiga karakter 8080:
{
// port configuration
"server": {
"port": 3000 // 80 in production
}
}
Itulah tepatnya yang tidak dapat dilakukan oleh "mengedit konfigurasi dengan ekspresi reguler": regex tidak tahu 8080 mana yang harus diubah, dan tidak tahu bahwa 8080 di dalam string harus dibiarkan.
3.3 Pesan error harus membawa offset
Kode error dari jsonc-parser adalah nilai enum; yang umum adalah:
| Kode error | Makna |
|---|---|
InvalidSymbol |
Muncul karakter yang seharusnya tidak ada |
PropertyNameExpected |
Diharapkan nama kunci |
ColonExpected |
Diharapkan titik dua |
CommaExpected |
Diharapkan koma |
CloseBraceExpected |
Objek tidak ditutup |
UnexpectedEndOfComment |
Komentar tidak ditutup |
InvalidEscapeCharacter |
Escape tidak valid |
Setelah offset dikonversi menjadi baris dan kolom, editor dapat menggambar garis bergelombang tepat pada posisi yang bermasalah.
4. Kapan menggunakan yang mana
| Skenario | Rekomendasi | Alasan |
|---|---|---|
| Body request/response API publik | JSON standar | Dialek tidak didukung klien serbaguna |
package.json, composer.json |
JSON standar | Manajer paket hanya memahami JSON standar |
tsconfig.json, .vscode/settings.json |
JSONC | Bawaan ekosistem, didukung native oleh toolchain |
| Konfigurasi aplikasi (butuh komentar) | JSONC atau YAML | Komentar harus dipertahankan sementara alat masih dapat membaca dan menulisnya |
Membutuhkan Infinity, heksadesimal, literal kompleks |
JSON5 | Daya ekspresi terkuat |
| Pertukaran data, log, cache | JSON per baris atau standar | Streaming dan universalitas diperlukan |
Aturan praktis: jika file akan dibaca oleh "alat non-manusia", utamakan JSON standar; pertimbangkan dialek hanya ketika Anda yakin file itu hanya dibaca oleh kode Anda sendiri.
5. Tiga jenis risiko yang diperkenalkan dialek
5.1 Kebutuhan yang merembes keluar
Ketika alat lain membaca file konfigurasi JSON5 dan memanggil JSON.parse, ia crash langsung. Gejala tipikalnya adalah "berfungsi baik secara lokal, gagal di Docker" — karena container menjalankan jalur pembacaan yang berbeda.
Penanggulangan: tambahkan pemeriksaan CI ke repositori yang secara eksplisit mencantumkan file mana yang diizinkan menggunakan dialek; sisanya divalidasi sebagai JSON standar.
5.2 Kejutan seputar inferensi tipe
JSON5 mengizinkan kunci tanpa tanda kutip dan tanda kutip tunggal, tetapi ia tidak membiarkan Anda memasukkan konstruksi literal seperti tanggal atau ekspresi reguler — ia tetap format data, bukan evaluasi JavaScript.
Orang pernah menganggap JSON5 dapat mengekspresikan new Date() hanya untuk melihat parsing gagal. JSON5 bukan eval.
5.3 Komentar hilang secara diam-diam
Baik JSON5.stringify maupun JSON.stringify tidak mempertahankan komentar. Jadi "baca, ubah sebuah nilai, tulis kembali" kehilangan setiap komentar.
Ketika komentar harus bertahan, satu-satunya opsi adalah API pengeditan jsonc-parser, atau pustaka YAML yang mendukung pelestarian komentar.
6. Rekomendasi praktis
- [ ] Pertahankan tabel "jenis file → format" dalam dokumentasi tim agar tidak ada yang berimprovisasi;
- [ ] Bungkus pembacaan konfigurasi dalam satu
loadConfig()yang memutuskan secara internal parser mana yang dipakai, sehingga kode bisnis tidak pernah memanggilJSON.parsesecara langsung; - [ ] Saat mengubah konfigurasi berkomentar secara programatik, gunakan
modify+applyEdits; jangan pernah menulis ulang seluruh file; - [ ] Validasi juga file dialek di CI (
jsonc-parseruntuk JSONC,JSON5.parseuntuk JSON5), jika tidak error sintaks hanya muncul saat runtime; - [ ] Gunakan JSON standar untuk antarmuka publik, kunci cache, dan payload message queue.
FAQ
Bagaimana cara memilih antara JSON5 dan JSONC?
Pilih berdasarkan runtime, bukan berdasarkan jumlah sintaks: VS Code, TypeScript dan ESLint membaca JSONC secara bawaan, sehingga JSONC memberi kompatibilitas terbaik; gunakan JSON5 hanya ketika Anda membutuhkan sintaks literal ES5.1 yang lebih luas, seperti tanda kutip tunggal, kunci tanpa tanda kutip, atau Infinity. Tidak ada dialek yang cocok untuk respons API publik.
Mengapa parser JSON standar tidak dapat membaca JSONC secara langsung?
JSONC mengizinkan komentar dan koma di akhir, sementara JSON.parse hanya menerima JSON RFC 8259 dan melempar error pada keduanya. Membaca JSONC memerlukan parser seperti jsonc-parser, atau menghapus komentar sebelum menyerahkan teks kepada parser standar.
Apakah komentar di JSONC tetap bertahan saat file diedit?
Ya, tetapi hanya melalui API pengeditan. modify dan applyEdits di jsonc-parser mengubah node target sambil mempertahankan komentar yang tersisa; mem-parse menjadi objek lalu men-stringify-nya menghilangkan setiap komentar.