Mở đầu — vì sao bài này quan trọng
Ở Bài 2, bạn đã làm quen với JSON như một định dạng dữ liệu cơ bản: cú pháp ngoặc nhọn, cặp key–value, và ý tưởng "máy đọc được, người cũng đọc được". Nhưng trong công việc thực tế của một Business Analyst, hiểu JSON ở mức "biết nó là gì" thôi thì chưa đủ. Khi bạn ngồi review một API spec, khi bạn viết acceptance criteria cho một ticket, hay khi bạn phải giải thích cho team dev tại sao một field bị thiếu lại làm hỏng cả luồng thanh toán — bạn cần hiểu JSON ở tầng sâu: các kiểu dữ liệu chính xác, cách dữ liệu lồng nhau (nested), cách mảng (array) hoạt động, và đặc biệt là JSON Schema — công cụ giúp bạn mô tả hợp đồng dữ liệu một cách chặt chẽ.
Tại sao điều này quan trọng đến vậy? Vì phần lớn lỗi tích hợp (integration bug) mà tôi từng chứng kiến trong các dự án ở Việt Nam không đến từ logic phức tạp, mà đến từ những hiểu lầm rất nhỏ về cấu trúc dữ liệu: một số điện thoại được gửi đi dưới dạng số 0901234567 bị mất số 0 đứng đầu; một giá trị true bị gửi thành chuỗi "true"; một field lẽ ra là mảng lại được gửi như một object đơn lẻ. BA nào nắm chắc JSON ở mức sâu sẽ phát hiện những vấn đề này ngay từ giai đoạn thiết kế spec, thay vì để chúng nổ ra trên môi trường production và tốn hàng giờ debug.
Bài này tập trung độc quyền vào: các kiểu dữ liệu của JSON, cấu trúc lồng nhau và mảng, cùng JSON Schema. Chúng ta không bàn về XML (Bài 13), không bàn về cách test bằng Postman (Bài 14) — chỉ đào thật sâu vào bản thân JSON.
Khái niệm cốt lõi
Sáu kiểu dữ liệu của JSON
JSON chỉ có đúng sáu kiểu dữ liệu. Đây là một sự đơn giản hóa có chủ đích, và việc nắm chắc ranh giới của từng kiểu giúp bạn tránh vô số lỗi.
1. String (chuỗi) — luôn nằm trong dấu nháy kép "...", không bao giờ là nháy đơn. Đây là điểm khác biệt quan trọng so với nhiều ngôn ngữ lập trình. Ví dụ: "Nguyễn Văn An", "0901234567", "true". Lưu ý: "true" là một chuỗi, hoàn toàn khác với true (giá trị boolean). Các ký tự đặc biệt trong chuỗi phải được escape bằng dấu \: ví dụ "Anh ấy nói \"xin chào\"" hoặc đường dẫn "C:\\Users\\an".
2. Number (số) — JSON không phân biệt số nguyên (integer) và số thực (float) ở cấp độ cú pháp. Tất cả đều là number: 42, 3.14, -1.5e10 (ký hiệu khoa học nghĩa là -1.5 × 10¹⁰). Điều này có một hệ quả thực tế rất lớn: nếu bạn cần phân biệt "số tiền là 100000 đồng" (số nguyên) với "tỷ giá là 100000.00" (số thực có 2 chữ số thập phân), JSON không tự làm điều đó cho bạn — bạn phải quy định trong spec. Ngoài ra, JSON không có khái niệm Infinity hay NaN; nếu hệ thống gửi những giá trị này, JSON sẽ không hợp lệ.
3. Boolean — chỉ có hai giá trị: true và false, viết thường, không có nháy. Đây là kiểu hay bị nhầm nhất với string.
4. null — biểu thị "không có giá trị" một cách chủ động. null khác với việc field bị thiếu hoàn toàn. Ví dụ, {"ngay_sinh": null} nghĩa là "chúng tôi biết về field ngày sinh nhưng người dùng chưa nhập", còn một object hoàn toàn không có key ngay_sinh lại có ý nghĩa khác. BA cần làm rõ sự khác biệt này trong spec.
5. Object — tập hợp các cặp key–value trong ngoặc nhọn {}. Key luôn là string. Thứ tự các key không được đảm bảo — đừng bao giờ viết logic phụ thuộc vào thứ tự field.
6. Array — danh sách có thứ tự trong ngoặc vuông []. Khác với object, thứ tự phần tử trong array có ý nghĩa và được giữ nguyên.
Nested: dữ liệu lồng nhau
Sức mạnh thực sự của JSON nằm ở khả năng lồng object trong object, array trong object, object trong array — không giới hạn độ sâu. Đây là cách JSON mô tả các thực thể phức tạp của đời thực.
{
"ma_don": "DH-2026-00837",
"khach_hang": {
"ho_ten": "Trần Thị Bích",
"dia_chi": {
"duong": "12 Nguyễn Huệ",
"quan": "Quận 1",
"thanh_pho": "TP. Hồ Chí Minh"
}
},
"tong_tien": 1250000,
"da_thanh_toan": true
}
Ở đây khach_hang là một object lồng bên trong, và bên trong nó lại có dia_chi là một object nữa. Khi đọc spec hoặc viết test case, bạn cần biết "đường dẫn" đến từng giá trị. Người ta thường dùng ký hiệu dấu chấm: khach_hang.dia_chi.thanh_pho trỏ tới giá trị "TP. Hồ Chí Minh". Hiểu cách diễn đạt đường dẫn này cực kỳ hữu ích khi bạn trao đổi với dev: thay vì nói mơ hồ "cái thành phố ấy", bạn nói chính xác "field khach_hang.dia_chi.thanh_pho".
Array: làm việc với danh sách
Array thường gây bối rối nhất cho BA mới. Một array có thể chứa các phần tử cùng kiểu hoặc khác kiểu, nhưng trong thực hành API tốt, chúng ta luôn giữ array đồng nhất — tất cả phần tử cùng một cấu trúc.
{
"ma_don": "DH-2026-00837",
"san_pham": [
{ "ten": "Son môi velvet", "so_luong": 2, "don_gia": 350000 },
{ "ten": "Phấn nước", "so_luong": 1, "don_gia": 550000 }
]
}
Đây là một array chứa hai object. Để trỏ tới đơn giá của sản phẩm đầu tiên, ta viết san_pham[0].don_gia (chỉ số bắt đầu từ 0, không phải 1 — một điểm cực kỳ dễ sai). Một array rỗng được viết là [], và nó khác với null: [] nghĩa là "có danh sách nhưng đang trống", còn null nghĩa là "không có danh sách nào cả".
JSON Schema: hợp đồng dữ liệu
Đây là phần nâng tầm bạn từ "người biết đọc JSON" thành "người thiết kế được JSON". JSON Schema là một chuẩn (bản thân nó cũng viết bằng JSON) dùng để mô tả và ràng buộc cấu trúc một JSON document phải tuân theo. Nó chính là phiên bản chính thức, máy đọc được, của những gì BA thường viết tay trong bảng đặc tả field.
{
"type": "object",
"required": ["ma_don", "tong_tien"],
"properties": {
"ma_don": { "type": "string", "pattern": "^DH-[0-9]{4}-[0-9]{5}$" },
"tong_tien": { "type": "number", "minimum": 0 },
"da_thanh_toan": { "type": "boolean" },
"san_pham": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["ten", "so_luong"],
"properties": {
"ten": { "type": "string", "maxLength": 200 },
"so_luong": { "type": "integer", "minimum": 1 }
}
}
}
}
}
Đọc schema trên, ta hiểu được: object phải có ma_don và tong_tien (bắt buộc); ma_don phải khớp mẫu định dạng cụ thể; tong_tien là số không âm; san_pham là array có ít nhất 1 phần tử, mỗi phần tử có ten và so_luong bắt buộc, và so_luong phải là số nguyên ≥ 1. Những từ khóa quan trọng BA nên thuộc: type, required, properties, items, enum (giới hạn trong tập giá trị cho phép), minimum/maximum, minLength/maxLength, pattern (biểu thức chính quy), và format (như email, date, uri).
Lợi ích lớn nhất: JSON Schema biến đặc tả dữ liệu thành thứ có thể kiểm tra tự động. Dev có thể dùng schema để validate dữ liệu vào ra, QA có thể dùng để test, và bản thân nó trở thành "sự thật chung" mà cả team đồng thuận — không còn cảnh mỗi người hiểu spec một kiểu.
Tình huống thực tế
Tình huống 1: Số 0 biến mất tại một ví điện tử
Một startup fintech tại Hà Nội (tạm gọi là PayViet) tích hợp với một ngân hàng để xác thực số tài khoản. BA của họ viết spec rằng số tài khoản gửi đi là number. Khi triển khai, tài khoản 0123456789 được gửi dưới dạng {"so_tk": 0123456789}. Vấn đề: trong JSON, số 0123456789 bị nhiều parser hiểu sai (số đứng đầu bằng 0 không hợp lệ ở dạng decimal), và ngay cả khi parse được thì số 0 đứng đầu cũng biến mất — thành 123456789. Kết quả là 4% giao dịch trong ngày đầu bị từ chối vì sai số tài khoản.
Bài học: Mọi định danh (số tài khoản, số điện thoại, mã đơn, CCCD) phải để kiểu string, không bao giờ là number — kể cả khi chúng "trông giống số". BA giỏi đặt câu hỏi ngay từ đầu: "Field này có cần làm phép tính số học không? Nếu không, để string."
Tình huống 2: Array hay object — một field, hai cách hiểu
Tại một sàn thương mại điện tử ở TP.HCM, team tích hợp với nhà cung cấp vận chuyển. Spec ghi field ghi_chu_giao_hang. Khi đơn có một ghi chú, đối tác gửi "ghi_chu_giao_hang": "Giao giờ hành chính". Khi đơn có nhiều ghi chú, họ gửi "ghi_chu_giao_hang": ["Giao giờ hành chính", "Gọi trước 15 phút"]. Hệ thống nhận lúc thì coi field là string, lúc thì là array, khiến code phía nhận crash ngẫu nhiên khoảng 200 đơn mỗi ngày.
Bài học: Một field phải có một kiểu duy nhất, ổn định. Nếu về bản chất nó là danh sách, hãy luôn để array — kể cả khi chỉ có một phần tử thì gửi ["Giao giờ hành chính"], và khi không có thì gửi []. JSON Schema với "type": "array" sẽ chặn ngay sự mơ hồ này ở khâu validate. Đây chính là lý do JSON Schema đáng giá.
Tình huống 3: null và field thiếu — sự khác biệt làm hỏng báo cáo
Một công ty SaaS quản lý nhân sự dùng API đồng bộ dữ liệu nhân viên. BA định nghĩa ngay_nghi_viec. Với nhân viên đang làm, một số bản ghi gửi "ngay_nghi_viec": null, một số bản ghi lại không có key này. Bộ phận làm báo cáo viết logic "đếm nhân viên đang làm = những người có ngay_nghi_viec là null". Hậu quả: những bản ghi thiếu hẳn field bị bỏ sót, báo cáo headcount sai lệch khoảng 60 người trong tổng số 1.200.
Bài học: BA phải quy định rõ trong spec: field này luôn xuất hiện và dùng null khi chưa có giá trị, hay được phép vắng mặt? Trong JSON Schema, bạn dùng required để bắt buộc field luôn có mặt, và cho phép kiểu ["string", "null"] để giá trị có thể là null. Sự rõ ràng này loại bỏ cả một lớp lỗi báo cáo.
Hướng dẫn từng bước
Khi bạn cần đặc tả hoặc review cấu trúc dữ liệu JSON cho một API, hãy làm theo trình tự sau:
- Liệt kê các thực thể và quan hệ. Trước khi viết JSON, hãy xác định: dữ liệu này mô tả cái gì (đơn hàng? người dùng?), và các thực thể con là gì (đơn hàng có nhiều sản phẩm → sản phẩm sẽ là array).
- Gán kiểu cho từng field. Với mỗi field, hỏi: Đây là chuỗi, số, boolean, hay null được phép? Quy tắc vàng: định danh và mã số luôn là string; chỉ dùng number khi cần tính toán.
- Quyết định bắt buộc hay tùy chọn. Field nào phải có (
required), field nào có thể vắng. Với field có thể trống, làm rõ là dùngnull, mảng rỗng[], hay vắng hẳn.
- Xác định ràng buộc. Giá trị có giới hạn không?
so_luongphải ≥ 1;trang_thaichỉ được nằm trong tập["cho_xu_ly", "dang_giao", "hoan_thanh"](dùngenum);emailphải đúngformat.
- Viết một ví dụ JSON mẫu. Luôn kèm một payload ví dụ đầy đủ trong spec. Dev và QA học từ ví dụ nhanh hơn rất nhiều so với đọc bảng đặc tả khô khan.
- Chuyển thành JSON Schema (nếu có thể). Dù bạn không cần tự viết schema hoàn chỉnh, hiểu các từ khóa cốt lõi giúp bạn review schema mà dev đưa ra và phát hiện thiếu sót.
- Kiểm chứng. Dán JSON mẫu vào một validator (ví dụ jsonlint.com để kiểm cú pháp, hoặc jsonschemavalidator.net để kiểm theo schema) để chắc chắn nó hợp lệ trước khi chốt spec.
Lỗi thường gặp & mẹo
- Dùng nháy đơn thay nháy kép.
{'ten': 'An'}không phải JSON hợp lệ. JSON bắt buộc nháy kép. - Dấu phẩy thừa (trailing comma).
{"a": 1, "b": 2,}— dấu phẩy sau phần tử cuối làm JSON không hợp lệ. Đây là lỗi phổ biến nhất khi viết tay. - Nhầm boolean với string.
"da_thanh_toan": "true"(string) khác"da_thanh_toan": true(boolean). Logic kiểm tra điều kiện sẽ chạy sai. - Để định danh là number. Như tình huống 1 — gây mất số 0 đứng đầu và lỗi tràn số với mã rất dài. Luôn để string.
- Nhầm
null,[], và field vắng mặt. Ba thứ này mang ba ý nghĩa khác nhau. Hãy quy định rõ trong spec. - Comment trong JSON. JSON không hỗ trợ comment (
//hay/ /). Đừng đưa comment vào payload thật. - Mẹo: Khi review spec, hãy đọc to từng field theo công thức "Tên field — kiểu — bắt buộc/tùy chọn — ràng buộc — ví dụ". Nếu thiếu bất kỳ phần nào, đó là chỗ spec còn lỗ hổng.
- Mẹo: Với key, hãy thống nhất một quy ước đặt tên xuyên suốt dự án (thường là
snake_casehoặccamelCase) — trộn lẫnngay_sinhvàngaySinhtrong cùng một API là nguồn lỗi âm thầm.
Bài tập thực hành
- Sửa JSON sai. Cho payload sau, hãy chỉ ra tất cả lỗi:
{'ma': DH001, "gia": "120000", "active": "true", "items": [],}. (Gợi ý: có ít nhất bốn vấn đề về cú pháp và kiểu dữ liệu.)
- Thiết kế cấu trúc. Bạn là BA cho một app đặt vé xem phim. Hãy viết một JSON mẫu mô tả một lượt đặt vé gồm: mã đặt chỗ, thông tin khách (tên, số điện thoại), suất chiếu (tên phim, giờ chiếu, rạp), và danh sách ghế đã chọn (mỗi ghế có mã ghế và giá). Chú ý áp dụng đúng quy tắc: số điện thoại là string, danh sách ghế là array.
- Đọc JSON Schema. Cho schema yêu cầu
trang_thailàenumgồm["moi", "da_xac_nhan", "da_huy"]vàso_luonglàintegervớiminimum: 1. Hãy viết một payload hợp lệ và một payload vi phạm schema, giải thích vì sao.
- Phân biệt ý nghĩa. Giải thích bằng lời sự khác nhau giữa ba trường hợp của field
discount: giá trị0, giá trịnull, và hoàn toàn không có keydiscount. Cho một bối cảnh kinh doanh cụ thể mà ba trường hợp này dẫn đến hành vi khác nhau.
Tóm tắt
JSON chỉ có sáu kiểu dữ liệu — string, number, boolean, null, object, array — nhưng chính sự đơn giản đó đòi hỏi BA phải kỷ luật trong việc gán kiểu chính xác. Định danh luôn để string; boolean không phải string; và null, mảng rỗng, field vắng mặt là ba thứ mang ba ý nghĩa khác nhau cần được quy định rõ. Cấu trúc nested và array cho phép JSON mô tả mọi thực thể phức tạp của đời thực, và việc thành thạo cách diễn đạt đường dẫn (khach_hang.dia_chi.thanh_pho, san_pham[0].don_gia) giúp bạn giao tiếp chính xác với team kỹ thuật. Cuối cùng, JSON Schema là công cụ biến đặc tả dữ liệu của bạn thành một hợp đồng máy đọc được, kiểm tra tự động được — nâng tầm BA từ người mô tả field bằng lời sang người thiết kế hợp đồng dữ liệu chặt chẽ. Ba tình huống thực tế trong bài đều cho thấy một sự thật: phần lớn lỗi tích hợp không nằm ở logic phức tạp, mà ở những hiểu lầm nhỏ về kiểu và cấu trúc dữ liệu — và đó chính là nơi một BA nắm chắc JSON tạo ra giá trị lớn nhất.