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 ý:

Đ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ố

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:

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:

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.