JSON

JavaScript Object Notation, định dạng trao đổi dữ liệu nhẹ dễ đọc cho cả con người và máy.

JSON (JavaScript Object Notation) là định dạng trao đổi dữ liệu nhẹ, dựa trên văn bản, biểu diễn dữ liệu dưới dạng các cặp khóa-giá trị. Cách viết này do Douglas Crockford tổng hợp năm 2001, còn việc chuẩn hóa thành quy chuẩn diễn ra sau đó. Lần chuẩn hóa đầu tiên là RFC 4627 năm 2006, tiếp theo là ECMA-404 phiên bản 1 năm 2013, rồi RFC 8259 cùng ECMA-404 phiên bản 2 năm 2017. Tính đến năm 2026, hai tài liệu cần tham chiếu là RFC 8259 và ECMA-404 phiên bản 2; cách diễn đạt khác nhau nhưng cú pháp được định nghĩa là cùng một thứ. Dù bắt nguồn từ cú pháp JavaScript, JSON là định dạng tổng quát không phụ thuộc ngôn ngữ và được hầu như mọi ngôn ngữ lập trình hỗ trợ, bao gồm Python, Java, Go và Ruby.

JSON hỗ trợ sáu kiểu dữ liệu: chuỗi (đặt trong dấu ngoặc kép), số (số nguyên và số thực dấu phẩy động), boolean (true/false), null, mảng (ngoặc vuông) và đối tượng (ngoặc nhọn). Sự đơn giản này là thế mạnh lớn nhất của JSON và cũng là lý do nó trở thành định dạng phản hồi tiêu chuẩn thực tế cho REST API.

So với XML, JSON không cần thẻ mở và thẻ đóng nên viết ngắn gọn hơn và phân tích cú pháp nhanh hơn. Cùng một dữ liệu sẽ ngắn hơn so với XML, nơi mỗi tên khóa phải viết hai lần ở thẻ mở và thẻ đóng. Tuy nhiên, mức giảm thay đổi rất nhiều theo độ dài tên khóa, độ sâu lồng nhau và việc có dùng thuộc tính hay không, nên không phải là một tỷ lệ cố định để ghi nhớ. Nếu muốn lấy kích thước làm căn cứ quyết định, hãy xuất dữ liệu thật ra cả hai định dạng và so sánh số byte sau khi nén gzip. Với dữ liệu có cùng cấu trúc lặp lại, gzip sẽ hấp thụ phần tên thẻ trùng lặp, nên chênh lệch sau nén không lớn như trước nén. Ngược lại, XML có hỗ trợ định nghĩa schema (XSD) và namespace hoàn thiện, nên vẫn được chọn khi cần xác thực dữ liệu nghiêm ngặt. YAML phiên bản 1.2 theo hướng bao hàm JSON như một tập cha nghiêm ngặt, nên văn bản JSON có thể đọc nguyên trạng như YAML. Vì có comment và anchor, YAML được ưa chuộng làm tệp cấu hình, nhưng cú pháp phụ thuộc thụt lề dễ gây lỗi khi sao chép và dán. Lưu ý quan hệ bao hàm này không hoàn toàn tương đương: chẳng hạn JSON có khóa trùng lặp là không hợp lệ theo đặc tả YAML (có triển khai báo lỗi, cũng có triển khai âm thầm chỉ giữ một giá trị).

JSON có một số hạn chế. Nó không hỗ trợ comment, vì vậy JSON5 hoặc JSONC (JSON with Comments) đôi khi được dùng cho tệp cấu hình. Không có kiểu ngày tháng, nên dữ liệu ngày giờ theo thông lệ được biểu diễn dưới dạng chuỗi ISO 8601 (ví dụ: "2025-01-15T09:30:00Z"). Nếu bỏ chữ Z hoặc phần lệch giờ ở cuối chuỗi, kết quả sẽ lệch tùy theo bên nhận hiểu là giờ địa phương hay UTC, nên hãy luôn ghi kèm múi giờ. Dấu phẩy cuối cũng không được phép: dấu phẩy sau phần tử cuối cùng của mảng hoặc đối tượng gây lỗi cú pháp.

Dù cú pháp đơn giản, đặc tả để nhiều chi tiết cho phía triển khai quyết định, nên trong thực tế hai điểm sau gây vướng nhiều nhất. Thứ nhất là độ chính xác của số. RFC 8259 không quy định số chữ số hay phạm vi, và đa số triển khai giả định số thực độ chính xác kép theo IEEE 754 (binary64). Cũng RFC này nêu số nguyên tương tác được tin cậy là đến 2 mũ 53 trừ 1, tức 9,007,199,254,740,991 (phía âm cũng cùng giá trị tuyệt đối). Truyền một ID vượt quá mức đó dưới dạng số sẽ làm tròn các chữ số thấp và trỏ sang bản ghi khác. ID hay số tài khoản 19 chữ số nên được truyền dưới dạng chuỗi ngay từ đầu để an toàn. Thứ hai là khóa trùng lặp. RFC 8259 chỉ nói tên trong một đối tượng nên là duy nhất, không cấm việc trùng lặp. Hành vi khi trùng lặp tùy triển khai: có nơi chỉ giữ giá trị xuất hiện cuối, có nơi báo lỗi, có nơi trả về tất cả. Vì đây cũng là thủ thuật để lách qua kiểm tra, phía tạo dữ liệu không được tạo khóa trùng và phía nhận cũng phải phát hiện và từ chối.

Trong thực tế, JSON Schema được dùng rộng rãi để xác thực và định nghĩa schema. Định nghĩa cấu trúc request và response của API bằng JSON Schema giúp hợp đồng dữ liệu giữa client và server rõ ràng và ngăn dữ liệu không hợp lệ lọt vào.

Về bảo mật, tuyệt đối không phân tích JSON từ nguồn không đáng tin bằng eval(). Luôn dùng JSON.parse() để ngăn thực thi mã độc. Ngoài ra, nếu ghép chuỗi để dựng JSON, dấu ngoặc kép hay ngoặc nhọn trong đầu vào có thể viết lại chính cấu trúc dữ liệu. Thay vì tự escape giá trị, hãy giao việc nhúng cho bộ tuần tự hóa của từng ngôn ngữ (với JavaScript là JSON.stringify()).

Từ góc độ đếm ký tự, các thành phần cú pháp JSON như tên khóa, ngoặc nhọn, ngoặc vuông, dấu ngoặc kép, dấu hai chấm và dấu phẩy đều ảnh hưởng đến kích thước dữ liệu. Về mã hóa ký tự, RFC 8259 quy định JSON trao đổi bên ngoài một hệ sinh thái khép kín phải được mã hóa UTF-8 và cấm thêm BOM ở đầu (bên nhận có thể bỏ qua BOM). Vì vậy ước lượng số byte chỉ cần tính theo UTF-8. Minify loại bỏ khoảng trắng và thụt lề không cần thiết để giảm kích thước, kết hợp với nén gzip có thể giảm đáng kể kích thước truyền tải. Với phản hồi chứa tiếng Nhật, số byte còn thay đổi tùy việc ghi nguyên ký tự hay escape thành dấu gạch chéo ngược kèm u và 4 chữ số thập lục phân (như \u3042). Một ký tự tiếng Nhật chiếm 3 byte trong UTF-8 nhưng 6 byte khi escape, nên nếu thư viện được cấu hình escape máy móc mọi ký tự không phải ASCII, phản hồi chủ yếu là tiếng Nhật sẽ phình lên khoảng 2 lần. Khi tối ưu kích thước phản hồi API, loại bỏ trường không cần thiết và rút ngắn tên khóa cũng là các kỹ thuật hiệu quả.

Chia sẻ bài viết này