Khác biệt giữa format JSON, kiểm tra cú pháp và JSON Schema trong thực tế

Đăng 2026-04-13 8 phút đọc

Tóm tắt (TL;DR)

Tháng trước, một team sửa một dòng trong feature_flags.json rồi deploy. JSON.parse chạy ngon, CI xanh, đẩy lên staging thì phát hiện flags.checkout_v2chuỗi "true", khiến mọi nhánh thanh toán rơi về phiên bản cũ. Câu nói “tôi đã kiểm tra JSON” thực chất gộp ba việc hoàn toàn khác nhau — format, kiểm tra cú pháp và kiểm tra cấu trúc — còn team đó chỉ làm hai bước đầu. Format (pretty-print) chỉ thêm khoảng trắng và xuống dòng để thay đổi hình thức, không kiểm chứng bất cứ điều gì. Kiểm tra cú pháp (syntax validation) soi ngoặc, dấu nháy và escape có đúng theo RFC 8259 hay không; chính JSON.parse làm việc này. Kiểm tra cấu trúc (structural validation) đến sau, hỏi “hình dáng này có phải là hình dáng mà code của tôi mong đợi không”, và JSON Schema ra đời chính xác vì lý do đó. Ngay khi bạn coi ba bước này là “một nút kiểm tra JSON duy nhất”, sự cố im lặng như trên xuất hiện. Format để đọc, parse khi nhận dữ liệu đầu vào, và ở các biên giới tin cậy như ranh giới API, file cấu hình và message bus, dùng bộ kiểm tra như Ajv để kiểm cả schema — pipeline an toàn là pipeline đặt ba bước vào đúng vị trí của chúng.

Bối cảnh và khái niệm

JSON được định nghĩa bởi RFC 8259 (và ECMA-404 tương đương). Cú pháp nhỏ và đơn giản. Một tài liệu là string, number, true, false, null, array hoặc object; string được bao bằng dấu nháy kép và hỗ trợ escape như \n, \t, \", \\, \uXXXX (unicode code unit). Number không phân biệt integer và float, tuân theo cú pháp thập phân. Object là tập hợp không có thứ tự các thành viên có khoá string, còn array là danh sách giá trị có thứ tự. Khoảng trắng ngoài string không có ý nghĩa.

Điều JSON cố ý không đưa vào cũng quan trọng. Không có comment, không có trailing comma, không có string dùng dấu nháy đơn, không có literal hex hoặc binary. Các ký tự ngoài ASCII phải là byte UTF-8 nguyên văn hoặc escape \u. JSON5 và HJSON nới lỏng một số quy tắc nhưng chúng là định dạng riêng, nên parser JSON nghiêm ngặt không chấp nhận.

Parse thành công chỉ có nghĩa là “đúng cú pháp”, không phải “đúng ngữ nghĩa”. {"user": "x", "pass": "y"} là JSON hoàn toàn hợp lệ, nhưng nếu endpoint mong đợi {"username": "...", "password": "..."} thì theo quan điểm ứng dụng đó vẫn là dữ liệu sai. Để bắt lỗi như vậy cần một schema — đặc tả máy đọc được về “hình dáng tài liệu được chấp nhận” — và chuẩn đó là JSON Schema (meta-schema khuyến nghị hiện tại là Draft 2020-12). Nó hỗ trợ field bắt buộc, type, enumconst, mẫu string bằng regex, phạm vi number, items và tính duy nhất của array, propertiesadditionalProperties của object, và tổ hợp bằng allOf, oneOf, anyOf, $ref. Các bộ kiểm tra như Ajv 8.12 biên dịch schema một lần thành function, nên chi phí kiểm tra trên hot path gần như không đáng kể — trong một API tôi vận hành, mỗi lần Ajv kiểm tra đã biên dịch trung bình chỉ vài chục micro giây.

Format đơn giản nhất trong ba việc. Nó không thay đổi ngữ nghĩa, chỉ điều chỉnh khoảng trắng và lùi dòng. JSON.stringify(obj, null, 2), jq . và formatter IDE đều làm việc này; nó dành cho con người đọc, máy không quan tâm.

So sánh và dữ liệu

Góc nhìnFormatKiểm tra cú phápKiểm tra JSON Schema
Mục đíchDàn khoảng trắng cho dễ đọcKiểm tra chuỗi có parse được thành JSON khôngKiểm tra giá trị đã parse có đúng hình dáng mong đợi không
Phát hiện đượcKhông có (chỉ sửa khoảng trắng)Ngoặc không khớp, lỗi dấu nháy, escape sai, trailing commaThiếu field bắt buộc, sai type, vượt phạm vi, khoá lạ
Không phát hiện đượcVấn đề cấu trúc hoặc ngữ nghĩaHình dáng mong đợi và quy tắc nghiệp vụQuy tắc không có trong schema, quan hệ giữa các field
Công cụ điển hìnhJSON.stringify(obj, null, 2), jq, formatter IDEJSON.parse, jq -e, parser của từng ngôn ngữAjv 8.x, python-jsonschema, validator OpenAPI
Vị trí áp dụngDevtool và logMọi ranh giới parse (ngầm định)Request/response API, nạp cấu hình, ranh giới message

Ba cột không phải quan hệ “chọn một” mà là các lớp tuần tự. Nhìn chằm chằm một package-lock.json 12 MB ở dạng một dòng rồi chuyển sang dạng có lùi dòng là format; thời điểm việc đó bị gọi thành “kiểm tra” là lúc sự cố bắt đầu. Format để đọc, parse để lọc chuỗi bị hỏng, và schema để kiểm tra hình dáng. Lỗ hổng phổ biến là áp hai bước đầu rồi bỏ qua bước cuối.

Tình huống thực tế

Tình huống 1 — Debug response API. Một cổng thanh toán bên thứ ba trả về JSON 600 dòng trong một dòng duy nhất. Panel Network của devtool trình duyệt, formatter cục bộ hoặc pipe curl ... | jq . sẽ chuyển nó về dạng người đọc được. Không có việc kiểm tra nào xảy ra ở đây; mục đích chỉ là “nhìn xem chỗ nào kỳ lạ”, và điều quan trọng là không gọi bước này là “đã kiểm tra”.

Tình huống 2 — Nạp file cấu hình. Giả sử dịch vụ đọc config.json khi khởi động. Parser JSON nghiêm ngặt sẽ bắt lỗi cú pháp như trailing comma và chặn khởi động, đó là hành vi đúng. Nhưng như trong ví dụ mở bài, nếu chỗ lẽ ra là retries: 3 lại có retries: "three", parse vẫn ổn và vấn đề chỉ lộ ra khi code cố so sánh chuỗi với số. Mẫu tôi khuyến nghị cho một team là: ngay dòng đầu khởi động, gọi function kiểm tra đã được tạo từ compile(schema) của Ajv để kiểm field bắt buộc, type, phạm vi, enum, và process.exit(1) khi vi phạm. Thay đổi năm phút đó giúp ngăn hai sự cố staging hai ngày mỗi sự cố sau đó.

Tình huống 3 — Kiểm tra hợp đồng OpenAPI. Giả sử team mô tả endpoint, request body và response trong components.schemas của tài liệu OpenAPI 3.1. Test hợp đồng kiểm payload mẫu từ đặc tả bằng cùng schema đó, bắt được drift kiểu “server trả integer vào chỗ đáng lẽ là string” trước khi client vỡ. Engine vẫn là cùng một JSON Schema validator với kiểm tra file cấu hình đơn độc; khác biệt chỉ là quy mô áp dụng.

Những hiểu lầm thường gặp

JSON.parse là đủ cho việc kiểm tra.” Parser chỉ nhìn cú pháp, không nhìn hình dáng. Nó vui vẻ trả về một object rỗng một nửa. Hãy dùng JSON.parse như vai trò “chặn chuỗi hỏng” và ở ranh giới tin cậy, thêm một lớp kiểm tra schema.

“JSON Schema chỉ dành cho server.” Kiểm ngay trên trình duyệt trước khi request giúp phản hồi người dùng ngay lập tức và giảm tải server. Ajv và nhiều validator khác chạy tốt trong trình duyệt. Kiểm tra server vẫn bắt buộc — đừng tin client — nhưng kiểm tra client-side cải thiện UX mà không làm yếu mô hình bảo mật.

“JSON5 là JSON có comment.” JSON5 thêm comment, khoá không cần dấu nháy, trailing comma, literal hex, v.v. Nó thân thiện làm định dạng cấu hình cho người sửa (tsconfig.json nổi tiếng thực ra là JSONC — một siêu tập phi chuẩn khác), nhưng consumer tuân thủ nghiêm ngặt RFC 8259 sẽ không chấp nhận. Chỉ dùng JSON5/JSONC khi consumer hỗ trợ rõ ràng; còn khi ra mạng hoặc qua parser tuỳ ý, hãy xuất JSON nghiêm ngặt.

“YAML là JSON dùng lùi dòng.” Mọi tài liệu JSON hợp lệ đều là YAML hợp lệ, nhưng YAML thêm anchor và alias, tag type, nhiều tài liệu trong một file, scalar block và folded, cú pháp nhạy với lùi dòng — những thứ JSON không có. Cái bẫy nổi tiếng là “vấn đề Na Uy”: YAML 1.1 đọc NO không dấu nháy thành boolean false. Quyết định “đổi JSON sang YAML cho dễ đọc” mời gọi các vấn đề mới.

Danh sách kiểm tra

  1. Bạn chỉ muốn đọc thôi? Chỉ format. Đừng gọi đó là “đã kiểm tra”.
  2. Dữ liệu có vượt qua ranh giới tin cậy không (request HTTP, message queue, file cấu hình)? Không chỉ parse mà cả JSON Schema.
  3. Bạn đã quyết định có chấp nhận field lạ không? Đặt additionalProperties: false trong schema và có chính sách từ chối hoặc loại bỏ.
  4. Thông điệp lỗi có thể hành động được không? Cấu hình validator để hiển thị mọi vi phạm cùng lúc như allErrors: true của Ajv.
  5. Schema có sống gần code thực sự không? Drift giữa đặc tả và triển khai sẽ ít xảy ra khi schema là single source of truth cho cả hai bên.
  6. Định dạng có hợp cho JSON không? Nếu là cấu hình người sửa và bạn tự chọn được parser, JSON5/JSONC/TOML khoan dung hơn. Nếu là trao đổi máy với máy, giữ JSON nghiêm ngặt.

Công cụ liên quan

JSON formatter của Patrache Studio chạy trong trình duyệt, nên payload bạn dán không rời khỏi máy — hữu ích khi xem response production có chứa thông tin cá nhân. Payload JSON hiếm khi đi một mình: nếu bên trong có byte đã mã hoá, Base64 và URL encoding: Khi nào cần và khi nào dùng sai giải thích tại sao kích thước tăng lên và khi nào nên chọn kênh vận chuyển khác. Nếu chứa ID, So sánh UUID v1, v4, v7 và thiết kế khoá chính DB sẽ nói về việc phiên bản mà server phát hành ảnh hưởng thế nào tới indexing, sort và cache của hệ thống hạ tầng.

Tài liệu tham khảo