JSON, TOML, YAML hay CSON: chọn định dạng cấu hình
Kết luận: Tệp cấu hình cần chú thích, ngày tháng và văn bản nhiều dòng, đó là những chỗ JSON thiếu. Hãy so sánh JSON, TOML, YAML và CSON, và xem chuyển đổi luôn làm mất những gì.

Kết luận: dùng JSON cho trao đổi máy-máy, và chọn TOML hoặc YAML cho cấu hình do con người duy trì tùy theo hệ sinh thái (YAML cho Kubernetes và CI, TOML cho cấu trúc phẳng hoặc ngày tháng gốc); chỉ quay về JSONC khi bạn không chắc bên đọc có hỗ trợ biến thể đó hay không.
JSON tồn tại cho máy móc trao đổi dữ liệu; tệp cấu hình được con người viết cho con người. Trộn lẫn hai thứ này và bạn sẽ có một
config.jsonkhông chứa được chú thích và chẳng ai dám rút gọn.
1. Trước hết, hãy phân biệt hai loại tệp
Trao đổi dữ liệu và cấu hình không cùng mục tiêu, dù cùng một người thường viết cả hai trên cùng một máy.
| Khía cạnh | Trao đổi dữ liệu | Cấu hình |
|---|---|---|
| Ai viết | Một chương trình tuần tự hóa giá trị | Con người, hoặc con người sửa đầu ra được sinh tự động |
| Ai đọc | Chương trình của người khác | Chương trình của chính bạn |
| Điều quan trọng | Không nhập nhằng, kích thước, tốc độ phân tích | Dễ đọc, có chú thích, diff dễ xem xét |
| Tần suất thay đổi | Mỗi yêu cầu | Thỉnh thoảng, có xem xét |
| Chi phí của một lỗi | Một phản hồi 500 | Một dịch vụ không khởi động được |
JSON gần như hoàn hảo ở cột bên trái: ngữ pháp tối giản, trình phân tích có trong mọi ngôn ngữ, và không có chú thích nên không có vấn đề "chú thích lệch khỏi giá trị". Ở cột bên phải, sự khắt khe của nó là ma sát thuần túy: không chú thích, không dấu phẩy cuối, không ngày tháng, và văn bản nhiều dòng phải gộp thành một dòng bằng \n.
2. Bốn ứng viên và cú pháp của chúng
- JSON: do RFC 8259 và ECMA-404 định nghĩa, với đúng sáu kiểu giá trị: object, array, string, number, boolean và
null. Đây là chuẩn trao đổi thực tế và là định dạng duy nhất không cần quyết định lựa chọn. - TOML: do Tom Preston-Werner tạo ra, với v1.0.0 công bố tháng 1 năm 2021. Mục tiêu đã nêu là một định dạng cấu hình tối giản với ngữ nghĩa rõ ràng, ánh xạ không nhập nhằng sang bảng băm.
Cargo.tomlvàpyproject.tomldùng nó mặc định. - YAML: đặc tả hiện tại là 1.2.2. Thụt lề biểu diễn phân cấp, và nó hỗ trợ chú thích, anchor cùng nhiều tài liệu. Manifest Kubernetes, GitHub Actions và Ansible đều phụ thuộc vào nó, với cái giá là đặc tả lớn và các khác biệt trong hiện thực.
- CSON: CoffeeScript Object Notation, viết cấu hình bằng literal đối tượng của CoffeeScript.
config.csoncủa Atom từng dùng nó. Nó hỗ trợ chú thích, khóa không ngoặc kép và dấu phẩy cuối, nhưng hệ sinh thái của nó chưa bao giờ ra khỏi thế giới JavaScript.
2.1 So sánh năng lực
| Năng lực | JSON | TOML | YAML | CSON |
|---|---|---|---|---|
| Chú thích | ✗ | ✓ # |
✓ # |
✓ # |
| Dấu phẩy cuối | ✗ | ✓ (mảng, bảng nội tuyến) | ✓ (tập hợp flow) | ✓ |
| Khóa không ngoặc kép | ✗ | ✓ | ✓ | ✓ |
| Ngày-giờ gốc | ✗ (chỉ chuỗi) | ✓ bốn kiểu ngày-giờ | ✓ (tự phân tích ở 1.1) | ✗ |
| Chuỗi nhiều dòng | ✗ (escape \n) |
✓ """ và ''' |
✓ scalar dạng khối và gấp | ✓ heredoc |
| Phạm vi số nguyên | Không giới hạn theo đặc tả, thực tế là double | 64-bit có dấu, phải không mất dữ liệu | Độ chính xác tùy ý cộng .inf/.nan |
Theo JavaScript |
| Lồng nhau | Ngoặc nhọn và ngoặc vuông | Bảng, mảng của bảng, khóa chấm | Thụt lề | Thụt lề |
| Nhạy cảm thụt lề | ✗ | ✗ | ✓ | ✓ |
null |
✓ | ✗ | ✓ | ✓ |
| Độ phủ công cụ | Mọi ngôn ngữ | Ngôn ngữ phổ biến | Mọi ngôn ngữ, hiện thực khác nhau | Chỉ JavaScript |
Hai ô đáng lưu ý. TOML không có null, đây là lỗi phổ biến nhất khi chuyển từ JSON. Hành vi "ngày tháng gốc" của YAML nghĩa là trình phân tích 1.1 tự biến 2026-10-31 thành đối tượng ngày, nên kiểu dữ liệu trôi khác nhau giữa các ngôn ngữ.
3. Chú thích: điểm yếu duy nhất của JSON
Kết luận trước: nếu con người duy trì tệp và cần giải thích vì sao một giá trị là như vậy, đừng dùng JSON chuẩn.
Có bốn hướng thực tế, xếp theo mức độ xâm lấn:
- Chuyển sang JSONC, tức JSON chuẩn cộng chú thích và dấu phẩy cuối.
settings.jsonvàtsconfig.jsoncủa VS Code dùng nó. Cái giá là bên đọc phải dùng trình phân tích hiểu chú thích nhưjsonc-parser;JSON.parsethuần sẽ lỗi. Chi tiết trong JSON5 và JSONC: JSON có chú thích. - Giữ một tài liệu song hành.
config.jsonvẫn máy đọc được, trong khiconfig.mdgiải thích từng trường nghĩa là gì và vì sao nó giữ giá trị đó. Cả hai nằm cùng thư mục và thay đổi trong cùng một commit. - Đưa giải thích vào lược đồ. Các từ khóa
descriptionvà$commentcủa JSON Schema có thể mô tả trường, nhưng lược đồ hiếm khi đi kèm cấu hình lúc chạy, nên người đọc cấu hình có thể không bao giờ thấy chúng. - Chuyển sang TOML hoặc YAML, để chính định dạng cung cấp chú thích mà mọi trình phân tích đều hiểu.
{
// Fixed at 8080: the frontend build script reads this port for its proxy
"port": 8080,
// Disable watching to avoid exhausting inodes in CI
"watch": false
}
JSONC vẫn là một biến thể: phần mở rộng giữ là .json, nhưng chỉ một số công cụ hiểu nó. Khi dữ liệu rời khỏi nhóm hoặc vượt qua ranh giới dịch vụ, ranh giới giữa JSONC và JSON chuẩn phải được ghi lại trong tài liệu.
4. Ngày tháng và văn bản nhiều dòng: sân nhà của TOML
TOML nâng ngày-giờ thành kiểu hạng nhất với bốn biến thể: ngày-giờ có offset, ngày-giờ địa phương, ngày địa phương và giờ địa phương. Chuỗi nhiều dòng có cú pháp riêng giữ nguyên xuống dòng:
title = "json-toolkit"
created = 2026-10-31T09:00:00+08:00
description = """
A JSON toolkit for developers.
Every operation runs locally in the browser.
"""
Cùng thông tin đó trong JSON phải hạ ngày tháng xuống chuỗi và escape xuống dòng:
{
"title": "json-toolkit",
"created": "2026-10-31T09:00:00+08:00",
"description": "A JSON toolkit for developers.\nEvery operation runs locally in the browser."
}
Trong JSON, "created" chỉ là một chuỗi tình cờ trông giống ngày tháng, nên thông tin kiểu phải nằm trong hợp đồng trường: cả hai bên phải đồng ý rằng trường này luôn theo RFC 3339. TOML cho trình phân tích một đối tượng ngày thật mà không cần thỏa thuận bằng lời.
YAML cũng chứa được văn bản nhiều dòng, nhưng hai kiểu khối mang ý nghĩa khác nhau: | giữ xuống dòng trong khi > gấp chúng thành dấu cách. Chọn sai kiểu thì một địa chỉ hoặc một câu lệnh SQL sẽ bị gộp thành một dòng, một lỗi rất khó phát hiện khi xem xét.
5. Số: khác biệt bị bỏ qua nhiều nhất
Đặc tả JSON không giới hạn độ chính xác số, nhưng RFC 8259 cũng cảnh báo rằng tương thích an toàn nhất khi số được xử lý như double IEEE 754. Trong JavaScript, số nguyên lớn hơn 2^53 - 1 mất độ chính xác một cách âm thầm:
JSON.parse('{"id":9007199254740993}').id
// 9007199254740992: the last digit is gone
TOML yêu cầu số nguyên 64-bit có dấu được xử lý không mất dữ liệu và phải báo lỗi thay vì cắt cụt âm thầm; số thực theo binary64 và có thể là inf hoặc nan. YAML 1.1 mang gánh nặng cũ: 1:30 được phân tích thành 90 (hệ lục thập phân), và boolean chấp nhận yes, no, on, off. Do đó có "vấn đề Na Uy" kinh điển, nơi mã quốc gia NO biến thành false.
Với tiền tệ, đừng bao giờ dựa vào dấu phẩy động trong bất kỳ định dạng nào ở trên: hãy dùng đơn vị tiền tệ nhỏ nhất dạng số nguyên, chuỗi, hoặc kiểu decimal của cơ sở dữ liệu. Định dạng và độ chính xác được trình bày sâu hơn trong ranh giới chuyển đổi JSON, YAML và XML.
6. Bảng và lồng nhau: ranh giới cứng của TOML
TOML biểu diễn lồng nhau bằng bảng và mảng của bảng:
[server]
port = 8080
[[routes]]
path = "/parse"
method = "POST"
[[routes]]
path = "/minify"
method = "POST"
Nó có hai ranh giới cứng bạn phải biết trước khi chuyển đổi:
- Gốc của một tài liệu TOML là một bảng, nên một tài liệu JSON có cấp cao nhất là mảng (như
[1, 2, 3]) không có biểu diễn TOML hợp lệ; nó phải được bọc thành{"items": [...]}; - TOML không có
null, nên một trường JSONnullphải bị bỏ hoặc thay bằng chuỗi rỗng hay giá trị đánh dấu, với ngữ nghĩa bị đẩy sang bên tiêu thụ.
YAML thì ngược lại: độ sâu lồng nhau không giới hạn, và anchor cộng merge key có thể tái sử dụng các đoạn:
defaults: &defaults
retries: 3
timeoutMs: 5000
server:
<<: *defaults
port: 8080
Anchor là năng lực chỉ YAML có và chắc chắn mất khi chuyển đổi: JSON không có cú pháp tham chiếu, nên tệp sau khi mở rộng sẽ lớn hơn và không có gì ghi lại rằng hai phần từng là cùng một cấu hình.
7. Chọn theo tình huống
| Tình huống | Khuyến nghị | Lý do |
|---|---|---|
| API công khai, hàng đợi tin nhắn, bộ nhớ đệm | JSON | Tính phổ quát tối đa, chi phí thương lượng bằng không |
| Cấu hình ứng dụng, phẳng và có kiểu | TOML | Chú thích, ngày tháng, số nguyên không mất dữ liệu, hành vi trình phân tích ổn định |
| Kubernetes, CI, Ansible | YAML | Hệ sinh thái đã cố định; đổi định dạng nghĩa là đổi công cụ |
VS Code, tsconfig.json |
JSONC | Được hỗ trợ sẵn, không cần đưa định dạng mới vào |
| Tái sử dụng các đoạn cấu hình | YAML | Anchor và merge key là lựa chọn sẵn có duy nhất |
| Sinh tự động và chỉ dành cho máy | JSON (đã nén) | Không cần chú thích, kích thước và chi phí phân tích tốt nhất |
| Dự án Atom hoặc CoffeeScript cũ | CSON, nhưng lên kế hoạch di chuyển | Hệ sinh thái rất hẹp và chi phí di chuyển tăng theo thời gian |
Một quy tắc ngón tay cái: hãy để hệ sinh thái chọn định dạng cấu hình, không phải sở thích cá nhân. Viết Rust nghĩa là Cargo.toml, triển khai lên Kubernetes nghĩa là YAML, và xây extension VS Code nghĩa là JSONC. Chỉ trong một dự án mới không có ràng buộc hệ sinh thái thì "cái nào viết dễ hơn" mới là câu hỏi thật.
8. Ranh giới chuyển đổi
| Hướng | Thông tin luôn bị mất | Lời khuyên |
|---|---|---|
| JSON → TOML | null, mảng cấp cao nhất, khóa không biểu diễn được |
Chuẩn hóa cấu trúc và bỏ trường rỗng trước |
| TOML → JSON | Chú thích, kiểu ngày tháng, inf / nan |
Chuyển ngày tháng thành chuỗi RFC 3339 |
| YAML → JSON | Chú thích, anchor và merge key, tag, nhiều tài liệu | Mở rộng anchor trước, rồi chuyển từng tài liệu |
| JSON → YAML | Không mất cấu trúc, nhưng chuỗi ngày có thể bị tự phân tích | Đặt chuỗi ngày trong ngoặc kép |
| CSON → JSON | Chú thích, biểu thức CoffeeScript | Di chuyển một lần; đừng đồng bộ hai chiều |
Công cụ chuyển đổi bảo đảm giá trị, không bao giờ bảo đảm chú thích. Vì vậy đừng thiết kế pipeline hai chiều kiểu "JSON là nguồn và YAML là sản phẩm build". Hoặc chỉ sinh theo một chiều, hoặc duy trì một tệp nguồn duy nhất.
9. Danh sách kiểm tra
- [ ] Ghi lại loại tệp nào dùng định dạng nào, và liệt kê các biến thể được phép;
- [ ] Ưu tiên TOML hoặc YAML cho cấu hình do con người duy trì, và JSON cho trao đổi máy-máy;
- [ ] Lưu ngày tháng trong JSON dưới dạng chuỗi RFC 3339 và đánh dấu định dạng trong lược đồ;
- [ ] Biểu diễn tiền tệ và số nguyên lớn hơn
2^53dưới dạng chuỗi hoặc đơn vị nhỏ nhất, không bao giờ dùng số thực; - [ ] Kiểm tra từng định dạng trong CI bằng trình phân tích riêng, không bao giờ dùng
JSON.parsecho YAML; - [ ] Chuyển định dạng chỉ theo một chiều, và xóa tệp cũ để hai bên không thể lệch nhau.
FAQ
Tại sao chuyển JSON sang TOML lại lỗi ở một số trường?
TOML không có null, và gốc của một tài liệu TOML là một bảng, nên một trường JSON null phải bị bỏ hoặc thay bằng chuỗi rỗng hay giá trị đánh dấu, còn mảng cấp cao nhất phải được bọc thành {"items": [...]}. Chú thích, kiểu ngày tháng và inf/nan cũng bị mất khi đi theo chiều ngược lại, đó là lý do việc chuyển đổi nên diễn ra một chiều.
Tôi có nên giữ hai tệp cấu hình ở hai định dạng khác nhau?
Không. Công cụ chuyển đổi bảo đảm giá trị nhưng không bao giờ bảo đảm chú thích, nên pipeline kiểu "JSON là nguồn và YAML là sản phẩm build" sẽ bị lệch. Hoặc chỉ sinh theo một chiều, hoặc duy trì một tệp nguồn duy nhất và xóa tệp cũ sau khi di chuyển.
Làm sao thêm chú thích vào tệp cấu hình JSON?
JSON chuẩn không có chú thích. Bạn có thể chuyển sang JSONC (biến thể của VS Code, đọc bằng jsonc-parser), giữ một tài liệu song hành giải thích từng trường, hoặc chuyển sang TOML hay YAML. Các tệp gửi cho hệ thống khác nên giữ JSON chuẩn, với giải thích nằm trong tài liệu hoặc lược đồ.
TOML hay YAML tốt hơn cho cấu hình ứng dụng?
Chọn TOML khi cấu trúc phẳng, chủ yếu là cặp khóa-giá trị và ngày tháng gốc có ý nghĩa; ngữ nghĩa của nó dễ dự đoán hơn và khác biệt giữa các trình phân tích nhỏ hơn. Chọn YAML khi bạn cần lồng sâu, nhiều tài liệu, tái sử dụng anchor, hoặc khi hệ sinh thái mục tiêu ưu tiên YAML như Kubernetes hay CI. Cả hai đều hỗ trợ chú thích, nên yếu tố khác biệt thật sự là độ phức tạp cấu trúc và mức cố định vào hệ sinh thái.
Tại sao NO biến thành false trong YAML?
YAML 1.1 coi yes, no, on, off, y và n là boolean, nên mã quốc gia NO của Na Uy bị phân tích thành false. Đây là "vấn đề Na Uy". Hãy tránh nó bằng cách dùng hiện thực theo YAML 1.2, hoặc đặt trong ngoặc kép mọi chuỗi có thể bị hiểu sai.
CSON còn đáng dùng không?
Không, với các dự án mới. CSON phụ thuộc vào hệ sinh thái CoffeeScript, ít công cụ được duy trì, và hỗ trợ trình biên tập cùng CI hạn chế; sau khi Atom ngừng được duy trì, các tình huống dùng nó càng thu hẹp thêm. Nếu một dự án đã có cấu hình CSON, hãy lên lịch di chuyển một lần sang JSON, TOML hoặc YAML thay vì viết thêm.