JSON5 dan JSONC: JSON dengan Komen
Inti pati: Kedua-dua dialek wujud kerana konfigurasi yang ditulis manusia memerlukan komen dan koma di hujung, tetapi sintaks dan jangkauan ekosistem mereka berbeza, jadi memilih yang salah menyekat rantaian alat anda.

Kesimpulan utama: gunakan JSONC untuk komen dalam konfigurasi VS Code dan TypeScript, gunakan JSON5 apabila anda memerlukan sintaks literal ES5.1 yang lebih luas seperti tanda petik tunggal atau kunci tanpa petik, dan kekalkan API awam pada JSON standard.
JSON standard tidak membenarkan komen, jadi setiap pasukan diam-diam tetap menulisnya — sehingga suatu hari binaan rosak. Daripada melakukannya secara rahsia, pilih dialek yang betul.
1. Kedua-dua dialek mempunyai motivasi berbeza
- JSONC (JSON with Comments): JSON standard ditambah komen dan koma di hujung, dengan hampir tiada perubahan sintaks lain. Ia ialah standard de facto ekosistem VS Code —
settings.json,tsconfig.jsondan.eslintrc.jsonsemuanya menggunakannya. - JSON5: jauh lebih bercita-cita tinggi, sejajar terus dengan literal objek ES5.1 — tanda petik tunggal, kunci tanpa petik, heksadesimal,
Infinity/NaN, penerusan baris dengan garis songsang, dan tanda+di hadapan.
Satu ayat untuk membezakan mereka: JSONC hanya menjadikan penulisan selesa; JSON5 membolehkan anda menulis fail konfigurasi seolah-olah ia JavaScript.
2. Jadual perbandingan sintaks
| Ciri | JSON Standard | JSONC | JSON5 |
|---|---|---|---|
Komen // /* */ |
✗ | ✓ | ✓ |
| Koma di hujung | ✗ | ✓ | ✓ |
| Rentetan bertanda petik tunggal | ✗ | ✗ | ✓ |
| Kunci tanpa petik | ✗ | ✗ | ✓ |
| Nombor heksadesimal | ✗ | ✗ | ✓ |
Titik perpuluhan di hadapan/belakang .5 5. |
✗ | ✗ | ✓ |
Nombor bermula dengan + |
✗ | ✗ | ✓ |
Infinity / NaN |
✗ | ✗ | ✓ |
Penerusan \ di hujung baris |
✗ | ✗ | ✓ |
| Rentetan berbilang baris | ✗ | ✗ | ✓ (dilaksanakan melalui penerusan) |
| Nilai peringkat atas sewenang-wenangnya | ✓ | ✓ | ✓ |
Spesifikasi penuh JSON5 hampir tidak melebihi satu halaman; pakej json5 telah lama diselenggara oleh komuniti, dan pelayar serta Node berkelakuan sama.
3. Penghuraian: dua API, dua jenis keupayaan penentuan kedudukan
3.1 JSON5: penghuraian dokumen penuh yang ringkas
import JSON5 from 'json5';
const config = JSON5.parse(`
{
// port perkhidmatan
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 maklumat offset — kerana ia tidak disasarkan kepada senario penyuntingan, hanya kepada "membaca konfigurasi".
3.2 JSONC: pokok sintaks dengan offset
Nilai jsonc-parser bukan terletak pada penghuraian tetapi pada penyuntingan. Ia menawarkan tiga lapisan API:
| API | Tujuan | Kegunaan tipikal |
|---|---|---|
parse |
Huraikan menjadi pokok yang membawa offset/length |
Penyerlahan sintaks, lipatan, pelayaran |
visit |
Laluan mendalam dahulu; panggil balik menerima laluan nod | Pemeriksaan seperti JSONPath |
modify + applyEdits |
Hasilkan suntingan teks minimum, kemudian gunakannya pada teks asal | Ubah nilai sambil mengekalkan komen |
import { parseTree, findNodeAtLocation, modify, applyEdits } from 'jsonc-parser';
const text = `{
// konfigurasi port
"server": {
"port": 8080 // 80 dalam produksi
}
}`;
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 mengekalkan komen asal dan menggantikan hanya tiga aksara 8080:
{
// konfigurasi port
"server": {
"port": 3000 // 80 dalam produksi
}
}
Itulah tepat apa yang "menyunting konfigurasi dengan ungkapan nalar" tidak boleh lakukan: ungkapan nalar tidak tahu 8080 mana yang perlu diubah, dan tidak tahu bahawa 8080 di dalam rentetan mesti dibiarkan.
3.3 Mesej ralat mesti membawa offset
Kod ralat daripada jsonc-parser ialah nilai enum; yang biasa ialah:
| Kod ralat | Maksud |
|---|---|
InvalidSymbol |
Aksara muncul yang sepatutnya tidak ada |
PropertyNameExpected |
Menjangka nama kunci |
ColonExpected |
Menjangka titik bertindih |
CommaExpected |
Menjangka koma |
CloseBraceExpected |
Objek tidak ditutup |
UnexpectedEndOfComment |
Komen tidak ditutup |
InvalidEscapeCharacter |
Escape tidak sah |
Setelah offset ditukar menjadi baris dan lajur, editor boleh melukis goresan tepat pada kedudukan yang salah.
4. Bila hendak menggunakan yang mana
| Senario | Syor | Sebab |
|---|---|---|
| Badan permintaan/respons API awam | JSON standard | Dialek tidak disokong oleh pelanggan serba guna |
package.json, composer.json |
JSON standard | Pengurus pakej hanya memahami JSON standard |
tsconfig.json, .vscode/settings.json |
JSONC | Lalai ekosistem, disokong secara asli oleh rantaian alat |
| Konfigurasi aplikasi (memerlukan komen) | JSONC atau YAML | Komen mesti dikekalkan manakala alat masih boleh membacanya |
Memerlukan Infinity, heksadesimal, literal kompleks |
JSON5 | Kuasa ekspresif paling kuat |
| Pertukaran data, log, cache | JSON berdelimit baris atau standard | Penstriman dan kesejagatan diperlukan |
Peraturan praktikal: jika fail akan dibaca oleh "alat bukan manusia", utamakan JSON standard; pertimbangkan dialek hanya apabila anda pasti ia dibaca semata-mata oleh kod anda sendiri.
5. Tiga jenis risiko yang diperkenalkan dialek
5.1 Limpahan keperluan
Apabila alat lain membaca fail konfigurasi JSON5 dan memanggil JSON.parse, ia ranap terus. Gejala tipikal ialah "berfungsi baik secara setempat, gagal pada Docker" — kerana kontena menjalankan laluan bacaan yang berbeza.
Langkah balas: tambah semakan CI ke dalam repositori yang menyenaraikan secara eksplisit fail mana yang dibenarkan menggunakan dialek; semua yang lain disahkan sebagai JSON standard.
5.2 Kejutan sekitar inferens jenis
JSON5 membenarkan kunci tanpa petik dan tanda petik tunggal, tetapi ia tidak membenarkan anda membawa masuk binaan literal seperti tarikh atau ungkapan nalar — ia masih format data, bukan penilaian JavaScript.
Orang pernah menganggap JSON5 boleh menyatakan new Date() hanya untuk melihat penghuraian gagal. JSON5 bukan eval.
5.3 Komen digugurkan secara senyap
Baik JSON5.stringify mahupun JSON.stringify tidak mengekalkan komen. Jadi "baca masuk, ubah sedikit nilai, tulis semula" menghilangkan setiap komen.
Apabila komen mesti bertahan, satu-satunya pilihan ialah API penyuntingan jsonc-parser, atau pustaka YAML yang menyokong pengekalan komen.
6. Cadangan praktikal
- [ ] Simpan jadual "jenis fail → format" dalam dokumentasi pasukan supaya tiada sesiapa membuat improvisasi;
- [ ] Balut pembacaan konfigurasi dalam satu
loadConfig()yang memutuskan secara dalaman penghurai mana untuk digunakan, supaya kod perniagaan tidak pernah memanggilJSON.parsesecara terus; - [ ] Semasa mengubah konfigurasi dengan komen secara programatik, gunakan
modify+applyEdits; jangan menulis semula keseluruhan fail; - [ ] Sahkan juga fail dialek dalam CI (
jsonc-parseruntuk JSONC,JSON5.parseuntuk JSON5), jika tidak ralat sintaks hanya muncul pada masa jalan; - [ ] Gunakan JSON standard untuk antara muka awam, kunci cache dan muatan baris gilir mesej.
Soalan Lazim
Bagaimana saya harus memilih antara JSON5 dan JSONC?
Pilih mengikut masa jalan dan bukannya bilangan sintaks: VS Code, TypeScript dan ESLint membaca JSONC secara lalai, jadi JSONC memberikan keserasian terbaik; gunakan JSON5 hanya apabila anda memerlukan sintaks literal ES5.1 yang lebih luas, seperti tanda petik tunggal, kunci tanpa petik atau Infinity. Tiada dialek yang sesuai untuk respons API awam.
Mengapa penghurai JSON standard tidak boleh membaca JSONC secara terus?
JSONC membenarkan komen dan koma di hujung, manakala JSON.parse menerima hanya JSON RFC 8259 dan melontar pada kedua-duanya. Membaca JSONC memerlukan penghurai seperti jsonc-parser, atau membuang komen sebelum menyerahkan teks kepada penghurai standard.
Patutkah komen dalam JSONC bertahan selepas suntingan fail?
Ya, tetapi hanya melalui API penyuntingan. modify dan applyEdits dalam jsonc-parser mengubah nod sasaran sambil mengekalkan komen yang tinggal; menghuraikan kepada objek dan menyirikannya semula menggugurkan setiap komen.