JSON Toolkit

JSON5 và JSONC: JSON có chú thích

Kết luận: Cả hai biến thể đều tồn tại vì cấu hình do con người viết cần chú thích và dấu phẩy cuối, nhưng cú pháp và độ phủ hệ sinh thái của chúng khác nhau, nên chọn sai sẽ làm tắc nghẽn chuỗi công cụ của bạn.

Người mớiĐọc khoảng 10 phútXuất bản 2026-10-30Cập nhật 2026-10-30
Ảnh bìa cho JSON5 và JSONC, JSON có chú thích

Kết luận: dùng JSONC cho chú thích trong cấu hình VS Code và TypeScript, dùng JSON5 khi bạn cần cú pháp literal ES5.1 rộng hơn như nháy đơn hay khóa không có ngoặc kép, và giữ API công khai ở JSON chuẩn.

JSON chuẩn không cho phép chú thích, nên mọi nhóm vẫn lặng lẽ viết chúng — cho tới một ngày bản build hỏng. Thay vì làm lén lút, hãy chọn một biến thể đúng đắn.

1. Hai biến thể có động cơ khác nhau

  • JSONC (JSON with Comments): JSON chuẩn cộng chú thích và dấu phẩy cuối, gần như không thay đổi cú pháp nào khác. Đây là chuẩn thực tế của hệ sinh thái VS Code — settings.json, tsconfig.json và .eslintrc.json đều dùng nó.
  • JSON5: tham vọng hơn nhiều, bám trực tiếp vào literal đối tượng ES5.1 — nháy đơn, khóa không có ngoặc kép, thập lục phân, Infinity/NaN, nối dòng bằng dấu gạch chéo ngược, và dấu + ở đầu.

Một câu để phân biệt: JSONC chỉ làm cho việc viết thoải mái hơn; JSON5 cho phép bạn viết tệp cấu hình như thể nó là JavaScript.

2. Bảng so sánh cú pháp

Tính năng JSON chuẩn JSONC JSON5
Chú thích // /* */ ✗ ✓ ✓
Dấu phẩy cuối ✗ ✓ ✓
Chuỗi dùng nháy đơn ✗ ✗ ✓
Khóa không có ngoặc kép ✗ ✗ ✓
Số thập lục phân ✗ ✗ ✓
Dấu thập phân ở đầu/cuối .5 5. ✗ ✗ ✓
Số bắt đầu bằng + ✗ ✗ ✓
Infinity / NaN ✗ ✗ ✓
Nối dòng bằng \ ở cuối dòng ✗ ✗ ✓
Chuỗi nhiều dòng ✗ ✗ ✓ (thực hiện qua nối dòng)
Giá trị cấp cao nhất tùy ý ✓ ✓ ✓

Đặc tả đầy đủ của JSON5 chỉ hơn một trang; gói json5 đã được cộng đồng duy trì từ lâu, và trình duyệt cùng Node hoạt động giống hệt nhau.

3. Phân tích: hai API, hai kiểu năng lực định vị

3.1 JSON5: phân tích toàn tài liệu đơn giản

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 trả về một đối tượng thuần không có thông tin vị trí — vì nó không nhắm tới tình huống chỉnh sửa, mà chỉ để "đọc cấu hình".

3.2 JSONC: cây cú pháp có vị trí

Giá trị của jsonc-parser không nằm ở phân tích mà ở chỉnh sửa. Nó cung cấp ba tầng API:

API Mục đích Cách dùng điển hình
parse Phân tích thành cây mang offset/length Tô màu cú pháp, thu gọn, điều hướng
visit Duyệt theo chiều sâu; callback nhận đường dẫn nút Kiểm tra kiểu JSONPath
modify + applyEdits Tạo chỉnh sửa văn bản tối thiểu, rồi áp lên văn bản gốc Đổi một giá trị mà giữ nguyên chú thích
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));

Kết quả giữ nguyên chú thích gốc và chỉ thay ba ký tự của 8080:

{
  // port configuration
  "server": {
    "port": 3000 // 80 in production
  }
}

Đó chính là điều "chỉnh sửa cấu hình bằng biểu thức chính quy" không làm được: regex không biết phải đổi 8080 nào, và cũng không biết 8080 nằm trong một chuỗi thì phải để yên.

3.3 Thông báo lỗi phải mang vị trí

Mã lỗi của jsonc-parser là giá trị enum; những mã phổ biến là:

Mã lỗi Ý nghĩa
InvalidSymbol Xuất hiện ký tự không nên có
PropertyNameExpected Mong đợi một tên khóa
ColonExpected Mong đợi dấu hai chấm
CommaExpected Mong đợi dấu phẩy
CloseBraceExpected Đối tượng chưa được đóng
UnexpectedEndOfComment Chú thích chưa được đóng
InvalidEscapeCharacter Escape không hợp lệ

Khi offset được chuyển thành dòng và cột, trình biên tập có thể vẽ đường gạch đúng ngay vị trí lỗi.

4. Khi nào dùng cái nào

Tình huống Khuyến nghị Lý do
Thân yêu cầu/phản hồi API công khai JSON chuẩn Các client đa dụng không hỗ trợ biến thể
package.json, composer.json JSON chuẩn Trình quản lý gói chỉ hiểu JSON chuẩn
tsconfig.json, .vscode/settings.json JSONC Mặc định của hệ sinh thái, chuỗi công cụ hỗ trợ sẵn
Cấu hình ứng dụng (cần chú thích) JSONC hoặc YAML Chú thích phải được giữ trong khi công cụ vẫn đọc và ghi được
Cần Infinity, thập lục phân, literal phức tạp JSON5 Khả năng biểu đạt mạnh nhất
Trao đổi dữ liệu, nhật ký, bộ nhớ đệm Định dạng theo dòng hoặc JSON chuẩn Cần truyền luồng và tính phổ quát

Quy tắc ngón tay cái: nếu tệp sẽ được một "công cụ không phải con người" đọc, hãy ưu tiên JSON chuẩn; chỉ cân nhắc biến thể khi bạn chắc chắn rằng chỉ mã của chính bạn đọc nó.

5. Ba loại rủi ro mà biến thể mang lại

5.1 Yêu cầu lan sang nơi khác

Khi một công cụ khác đọc tệp cấu hình JSON5 rồi gọi JSON.parse, nó sập ngay. Triệu chứng điển hình là "chạy tốt ở máy cá nhân, hỏng trên Docker" — vì container chạy một đường đọc khác.

Biện pháp: thêm kiểm tra trong CI trên kho mã, liệt kê rõ những tệp được phép dùng biến thể; mọi tệp còn lại được kiểm tra như JSON chuẩn.

5.2 Bất ngờ quanh suy luận kiểu

JSON5 cho phép khóa không ngoặc kép và nháy đơn, nhưng không cho phép đưa vào các cấu trúc literal như ngày tháng hay biểu thức chính quy — nó vẫn là định dạng dữ liệu, không phải đánh giá JavaScript. Người ta từng cho rằng JSON5 biểu diễn được new Date() rồi chỉ thấy phân tích lỗi. JSON5 không phải eval.

5.3 Chú thích bị âm thầm loại bỏ

Cả JSON5.stringify lẫn JSON.stringify đều không giữ chú thích. Vậy nên "đọc vào, sửa một giá trị, ghi lại" sẽ mất toàn bộ chú thích. Khi chú thích buộc phải tồn tại, lựa chọn duy nhất là API chỉnh sửa của jsonc-parser, hoặc một thư viện YAML có hỗ trợ giữ chú thích.

6. Khuyến nghị thực tế

  • [ ] Giữ một bảng "loại tệp → định dạng" trong tài liệu của nhóm để không ai tự phát minh;
  • [ ] Bọc việc đọc cấu hình trong một hàm loadConfig() duy nhất, tự quyết định bên trong dùng trình phân tích nào, để mã nghiệp vụ không bao giờ gọi JSON.parse trực tiếp;
  • [ ] Khi chỉnh sửa cấu hình có chú thích bằng chương trình, dùng modify + applyEdits; đừng bao giờ ghi lại toàn bộ tệp;
  • [ ] Cũng kiểm tra tệp biến thể trong CI (jsonc-parser cho JSONC, JSON5.parse cho JSON5), nếu không lỗi cú pháp chỉ lộ ra lúc chạy;
  • [ ] Dùng JSON chuẩn cho giao diện công khai, khóa bộ nhớ đệm và payload của hàng đợi tin nhắn.

FAQ

Tôi nên chọn JSON5 hay JSONC?

Hãy chọn theo môi trường chạy chứ không theo số lượng cú pháp: VS Code, TypeScript và ESLint đọc JSONC mặc định, nên JSONC cho khả năng tương thích tốt nhất; chỉ dùng JSON5 khi bạn cần cú pháp literal ES5.1 rộng hơn, như nháy đơn, khóa không ngoặc kép hay Infinity. Cả hai biến thể đều không phù hợp cho phản hồi API công khai.

Tại sao trình phân tích JSON chuẩn không đọc trực tiếp được JSONC?

JSONC cho phép chú thích và dấu phẩy cuối, trong khi JSON.parse chỉ chấp nhận JSON theo RFC 8259 và báo lỗi ở cả hai trường hợp. Đọc JSONC cần trình phân tích như jsonc-parser, hoặc loại bỏ chú thích trước khi đưa văn bản cho trình phân tích chuẩn.

Chú thích trong JSONC có được giữ lại khi sửa tệp không?

Có, nhưng chỉ qua một API chỉnh sửa. modify và applyEdits của jsonc-parser thay đổi nút được nhắm tới trong khi giữ các chú thích còn lại; phân tích thành đối tượng rồi stringify sẽ làm mất mọi chú thích.