YAML và JSON: khác biệt, ví dụ và khi nào nên dùng
YAML và JSON khác nhau thế nào? Tóm lại, cả hai là định dạng văn bản cho cùng kiểu dữ liệu (đối tượng, danh sách, chuỗi, số, boolean và null). JSON chặt chẽ, dùng ngoặc nhọn và dấu nháy, là mặc định cho API web. YAML dùng thụt lề, cho phép chú thích, anchor và nhiều tài liệu trong một tệp, phổ biến cho cấu hình. YAML 1.2 gần như là tập cha của JSON.
Dùng thử miễn phí: Trình chuyển đổi YAML sang JSON Miễn phí, không cần tài khoản.
JSON (JavaScript Object Notation, được định nghĩa trong RFC 8259) được thiết kế để các chương trình trao đổi dữ liệu với nhau. Vậy YAML là gì? Tên của nó là viết tắt của “YAML Ain't Markup Language”, và nó được thiết kế để con người đọc và chỉnh sửa dữ liệu bằng tay. Bài viết này trình bày cùng một dữ liệu ở cả hai định dạng, so sánh chúng và điểm qua những cái bẫy của từng định dạng. Để xem một tệp bất kỳ trông thế nào ở định dạng còn lại, hãy dán nó vào trình chuyển đổi YAML sang JSON hoặc trình chuyển đổi JSON sang YAML miễn phí.
Cùng một dữ liệu trong JSON và YAML
Đây là một tệp cấu hình ứng dụng nhỏ viết bằng JSON:
{
"name": "web-app",
"version": "2.4.1",
"replicas": 3,
"debug": false,
"database": {
"host": "db.example.com",
"port": 5432
},
"features": ["login", "search"],
"maintainer": null
}
Và cùng dữ liệu đó trong YAML, kèm thêm một dòng chú thích mà JSON không thể chứa:
# Settings for the web app
name: web-app
version: 2.4.1
replicas: 3
debug: false
database:
host: db.example.com
port: 5432
features:
- login
- search
maintainer: null
Cả hai đều được phân tích thành cùng một đối tượng. JSON đánh dấu cấu trúc bằng {}, [], dấu phẩy và dấu nháy; YAML đánh dấu bằng thụt lề, các cặp key: value và - cho phần tử danh sách. Phần lớn chuỗi trong YAML hoàn toàn không cần dấu nháy, vì thế nó đọc giống một tệp thiết lập hơn là mã nguồn.
YAML và JSON: những khác biệt chính
| JSON | YAML | |
|---|---|---|
| Cấu trúc | Ngoặc nhọn, ngoặc vuông và dấu phẩy | Thụt lề (chỉ dùng dấu cách) |
| Chú thích | Không cho phép | # comment |
| Chuỗi | Luôn nằm trong dấu nháy kép | Thường không cần nháy; dùng nháy 'single' hoặc "double" khi cần |
| Kiểu dữ liệu | Đối tượng, mảng, chuỗi, số, boolean, null | Tương tự, cộng thêm tag tùy chọn, timestamp trong một số schema và kiểu tự định nghĩa |
| Tái sử dụng | Không có | Anchor &, alias * và merge key << |
| Nhiều tài liệu trong một tệp | Không (mỗi tệp một giá trị) | Có, phân tách bằng --- |
| Văn bản nhiều dòng | Chỉ bằng ký tự thoát \n |
Block scalar | và > |
| Phân tích cú pháp | Ngữ pháp nhỏ, chặt chẽ; tích hợp sẵn trong trình duyệt và nhiều thư viện chuẩn | Ngữ pháp lớn hơn; thường cần thư viện bên thứ ba |
| Ứng dụng điển hình | REST API, package.json, log, dữ liệu gửi giữa các chương trình |
Manifest Kubernetes, workflow GitHub Actions, Docker Compose, Ansible |
Một số định dạng chấp nhận cả hai: tài liệu OpenAPI có thể viết bằng một trong hai, và kubectl apply đọc được manifest JSON lẫn YAML. Nói chung, JSON thắng khi chương trình ghi và đọc dữ liệu; YAML thắng khi con người bảo trì dữ liệu bằng tay và cần chú thích.
YAML có phải là tập cha (superset) của JSON không?
Gần như vậy. Đặc tả YAML 1.2 (2009) đặt mục tiêu biến YAML thành tập cha chặt chẽ của JSON, và trên thực tế một trình phân tích YAML 1.2 đọc được gần như mọi tài liệu JSON và trả về cùng dữ liệu. Có hai lưu ý:
- Khóa trùng lặp. RFC 8259 chỉ nói khóa của đối tượng nên là duy nhất, nhưng YAML bắt buộc điều đó, nên
{"a": 1, "a": 2}bị trình phân tích YAML nghiêm ngặt từ chối. - Trình phân tích YAML 1.1. Nhiều thư viện được dùng rộng rãi vẫn tuân theo quy tắc cũ của YAML 1.1. Ví dụ, PyYAML chỉ nhận diện số thực có chứa dấu chấm, nên số JSON
1e3bị trả về thành chuỗi"1e3".
Điều ngược lại thì không đúng: phần lớn YAML không phải là JSON hợp lệ.
Những cái bẫy của YAML cần biết
Vấn đề Na Uy (Norway problem): NO biến thành false
Trong YAML 1.1, các từ không có nháy yes, no, on và off (viết thường, viết hoa chữ đầu hoặc viết hoa toàn bộ) đều là boolean, cùng với true và false. Vì vậy danh sách mã quốc gia này:
countries:
- GB
- NO
- SE
được nạp thành ["GB", false, "SE"] trong một trình phân tích YAML 1.1 như PyYAML. Cũng quy tắc này biến khóa on: của một workflow GitHub Actions thành boolean True khi bạn nạp tệp bằng PyYAML. Core schema của YAML 1.2 đã khắc phục điều này: chỉ true và false (cũng có thể viết True hoặc TRUE) là boolean, nên trình phân tích 1.2 giữ NO là chuỗi. Vì bạn hiếm khi kiểm soát được trình phân tích nào sẽ đọc tệp của mình, thói quen an toàn là đặt các giá trị như vậy trong dấu nháy: - "NO".
Những con số không nên là số
- Số 0 ở đầu. YAML 1.1 đọc
0755là số bát phân, tức 493. YAML 1.2 đọc nó là số thập phân 755 và viết số bát phân dưới dạng0o755. Một mã bưu chính như01234sẽ thành 668 hoặc 1234, tùy trình phân tích. Hãy đặt nó trong dấu nháy. - Số phiên bản.
python-version: 3.10là số thực 3.1, không phải “3.10”. Hãy viết"3.10". - Số 0 ở cuối.
1.0là số thực, nên nó thành1khi được chuyển sang JSON trong JavaScript.
Tab và khoảng trắng mang ý nghĩa
Đặc tả YAML cấm dùng ký tự tab để thụt lề, nên một tab ở đầu dòng là lỗi cú pháp chứ không phải vấn đề phong cách. Thụt lề cũng mang ý nghĩa: dịch một khóa sang trái hai dấu cách sẽ chuyển nó sang một đối tượng cha khác, và tệp vẫn hoàn toàn hợp lệ nhưng lại mang nghĩa khác.
Chuỗi nhiều dòng: | và >
Khối literal (|) giữ nguyên các ngắt dòng; khối folded (>) nối các dòng bằng dấu cách:
literal: |
Line one
Line two
folded: >
This long sentence is
folded into one line.
literal trở thành "Line one\nLine two\n" và folded trở thành "This long sentence is folded into one line.\n". Cả hai đều giữ một ký tự xuống dòng ở cuối; viết |- hoặc >- để bỏ nó.
Anchor, alias và merge key
YAML có thể định nghĩa một khối một lần rồi tái sử dụng:
defaults: &defaults
adapter: postgres
port: 5432
production:
<<: *defaults
host: db.example.com
&defaults đặt tên cho khối, *defaults tham chiếu đến nó, và << gộp các khóa của nó vào, nên cuối cùng production có adapter, port và host. JSON không có cơ chế tương đương: khi chuyển sang JSON, các giá trị được sao chép vào mọi chỗ chúng được dùng. Merge key xuất phát từ YAML 1.1 và không thuộc core schema của 1.2, nhưng hầu hết các trình phân tích phổ biến vẫn hỗ trợ.
Những cái bẫy của JSON cần biết
JSON rất chặt chẽ, và ba quy tắc sau gây ra phần lớn lỗi:
- Không có chú thích.
//và/* */là lỗi cú pháp. - Không có dấu phẩy thừa ở cuối.
["a", "b",]không hợp lệ. - Chỉ dùng dấu nháy kép. Khóa phải nằm trong nháy, và không được dùng nháy đơn.
Vì vậy tệp này sẽ bị JSON.parse báo lỗi:
{
// port for local development
'port': 8080,
"tags": ["api", "v2",],
}
Với các tệp viết tay, có những biến thể nới lỏng: JSONC (“JSON with comments”) được dùng trong thiết lập VS Code và tsconfig.json, còn JSON5 cho phép thêm nháy đơn, khóa không có nháy và dấu phẩy ở cuối. Trình phân tích JSON chuẩn không chấp nhận biến thể nào trong số này. Công cụ Định dạng JSON chấp nhận đầu vào JSON5, đánh dấu nó là “JSON5 hợp lệ — đã chuyển sang JSON chuẩn” và trả lại cho bạn JSON chuẩn.
.yaml và .yml
YAML và YML khác nhau thế nào? Đây không phải là câu hỏi về định dạng: cả hai phần mở rộng đều chỉ cùng một định dạng, và trình phân tích không quan tâm bạn dùng cái nào. RFC 9512, văn bản đăng ký media type application/yaml vào năm 2024, gọi .yaml là phần mở rộng được ưu tiên và lưu ý rằng .yml vẫn đang được dùng. FAQ của dự án YAML cũng khuyến nghị .yaml, và Docker Compose tìm compose.yaml trước compose.yml. GitHub Actions chấp nhận cả hai trong .github/workflows. Hãy chọn một kiểu cho mỗi dự án và đừng để config.yaml và config.yml nằm cạnh nhau.
Bảo mật: nạp YAML không đáng tin cậy một cách an toàn
Các trình nạp YAML đầy đủ có thể tạo đối tượng đặc thù của ngôn ngữ từ tag. Trong Python, yaml.load(data, Loader=yaml.UnsafeLoader) có thể tạo đối tượng Python tùy ý và, với một tệp được chế tác có chủ đích, thực thi mã. Luôn dùng yaml.safe_load() cho những tệp không phải do bạn viết; hàm này chỉ tạo dict, list, chuỗi, số, boolean và null thông thường. Từ PyYAML 6.0, yaml.load() từ chối chạy nếu không chỉ định rõ Loader. Cũng cần cẩn thận với các alias lồng nhau sâu (tệp “billion laughs”) có thể bung ra thành cấu trúc khổng lồ. JSON không có tag hay alias, nên JSON.parse và json.loads của Python luôn chỉ trả về dữ liệu thuần.
Chuyển đổi giữa YAML và JSON
Cả hai trình chuyển đổi đều chạy hoàn toàn trong trình duyệt của bạn và dùng thư viện mã nguồn mở yaml cho JavaScript.
Trình chuyển đổi YAML sang JSON phân tích YAML 1.2, triển khai anchor và alias, áp dụng merge key <<, và biến một tệp có nhiều tài liệu --- thành một mảng JSON. Bạn có thể chọn 2, 3 (mặc định) hoặc 4 dấu cách, tab hoặc đầu ra rút gọn (minified), sắp xếp khóa theo thứ tự bảng chữ cái, và tải kết quả về dưới dạng converted.json. Lỗi cú pháp, chẳng hạn dùng tab để thụt lề, được hiển thị kèm số dòng và số cột. Chú thích sẽ bị mất, vì JSON không thể lưu chúng.
Trình chuyển đổi JSON sang YAML đọc JSON chuẩn và JSON5, nên chú thích và dấu phẩy ở cuối trong đầu vào được chấp nhận nhưng không được giữ lại. Công cụ xuất YAML với thụt lề hai dấu cách và không có tùy chọn nào. Đầu ra tuân theo YAML 1.2: các chuỗi như "0755" và "true" được đặt trong nháy, nhưng NO, yes hay on vẫn không có nháy vì trong 1.2 chúng là chuỗi bình thường. Nếu tệp sẽ được một công cụ YAML 1.1 như PyYAML đọc, hãy tự thêm dấu nháy cho các giá trị đó.
Trên dòng lệnh, yq của Mike Farah chuyển đổi theo cả hai chiều, còn jq kiểm tra và định dạng đẹp JSON:
yq -o json config.yaml
yq -P -oy config.json
jq . config.json
Khi nào dùng YAML và khi nào dùng JSON
Việc chọn giữa JSON và YAML thường phụ thuộc vào việc ai là người viết tệp:
- Dùng JSON cho dữ liệu mà các chương trình trao đổi với nhau: request và response của API, thông điệp giữa các dịch vụ, log, bộ nhớ trình duyệt và mọi thứ do mã sinh ra. JSON không mơ hồ và được hỗ trợ ở mọi nơi.
- Dùng YAML cho cấu hình mà con người chỉnh sửa và review: manifest triển khai, pipeline CI, tệp Compose. Chú thích và cú pháp gọn nhẹ hơn giúp diff dễ đọc hơn.
- Làm theo công cụ. Nếu một nền tảng yêu cầu YAML (GitHub Actions) hoặc JSON (
package.json), hãy dùng đúng định dạng đó thay vì chuyển đổi.
Nếu chọn YAML, hãy giữ nó thật “nhàm chán”: thụt lề hai dấu cách, đặt nháy quanh mọi thứ có thể bị đọc thành boolean hoặc số, và dùng một linter như yamllint trong CI.
Câu hỏi thường gặp
YAML có tốt hơn JSON không?
Nhìn chung, không bên nào tốt hơn. JSON đơn giản và chặt chẽ hơn, nên là lựa chọn an toàn hơn cho dữ liệu trao đổi giữa các chương trình. YAML dễ đọc và chỉnh sửa hơn đối với con người và hỗ trợ chú thích, đó là lý do rất nhiều tệp cấu hình dùng nó.
Có thể dùng JSON bên trong tệp YAML không?
Có. Kiểu flow của YAML dùng cùng loại ngoặc như JSON, nên ports: [80, 443] hoặc db: {"host": "localhost"} đều hoạt động trong tệp YAML, và trình phân tích YAML 1.2 chấp nhận gần như mọi tài liệu JSON hoàn chỉnh. Khóa trùng lặp là ngoại lệ chính.
JSON có chú thích được không?
JSON chuẩn thì không: RFC 8259 không có cú pháp chú thích, và JSON.parse báo lỗi khi gặp // hoặc /* */. Một số công cụ chấp nhận JSONC hoặc JSON5, vốn cho phép chú thích, nhưng bạn phải xóa chúng trước khi đưa tệp cho một trình phân tích nghiêm ngặt.
YAML và YML khác nhau thế nào?
Về nội dung thì không khác gì: .yaml và .yml là hai phần mở rộng tệp của cùng một định dạng. RFC 9512 xác định .yaml là phần mở rộng được ưu tiên, nhưng trình phân tích YAML không quan tâm đến phần mở rộng.
Vì sao YAML biến NO hoặc on thành false hoặc true?
Vì YAML 1.1 coi yes, no, on và off là boolean. YAML 1.2 chỉ coi true và false là boolean, nhưng nhiều trình phân tích, trong đó có PyYAML, vẫn dùng quy tắc 1.1. Hãy đặt các giá trị như vậy trong dấu nháy, ví dụ country: "NO", và mọi trình phân tích sẽ đọc chúng là chuỗi.
Dùng thử miễn phí: Trình chuyển đổi YAML sang JSON Miễn phí, không cần tài khoản.