Thực Hành Tốt Nhất Về Định Dạng JSON
JSON (JavaScript Object Notation) đã trở thành tiêu chuẩn thực tế cho trao đổi dữ liệu trên web. API trả về JSON, file cấu hình sử dụng JSON, thậm chí cơ sở dữ liệu cũng lưu trữ tài liệu JSON. Mặc dù JSON đơn giản, nó có các quy tắc cú pháp nghiêm ngặt dễ bị vi phạm, và JSON định dạng không đúng dẫn đến khó gỡ lỗi, vấn đề hiệu suất và lỗ hổng bảo mật. Hướng dẫn này bao gồm mọi thứ bạn cần biết về định dạng JSON đúng cách, từ cú pháp cơ bản đến xác thực Schema và xử lý file lớn.
JSON Là Gì?
JSON là định dạng trao đổi dữ liệu dựa trên văn bản, nhẹ, bắt nguồn từ cú pháp object literal của JavaScript. Nó được Douglas Crockford chỉ định vào đầu những năm 2000 và được chuẩn hóa thành ECMA-404 và RFC 8259. Mục tiêu thiết kế của JSON là tính đơn giản, khả năng đọc của con người và dễ triển khai trên các ngôn ngữ lập trình. Ngày nay, mọi ngôn ngữ lập trình chính đều có hỗ trợ JSON tích hợp.
JSON hỗ trợ sáu kiểu dữ liệu: chuỗi (dấu ngoặc kép), số (số nguyên và số thực), boolean (true và false), null, đối tượng (tập hợp khóa-giá trị không có thứ tự) và mảng (danh sách có thứ tự). Nó không hỗ trợ chú thích, ngày tháng, dữ liệu nhị phân hoặc giá trị undefined.
Quy Tắc Cú Pháp JSON
JSON có cú pháp phải được tuân thủ nghiêm ngặt. Chỉ một lỗi ký tự cũng có thể khiến toàn bộ tài liệu không phân tích được. Hiểu các quy tắc này ngăn chặn các lỗi định dạng phổ biến nhất.
Chuỗi Phải Dùng Dấu Ngoặc Kép
Tất cả giá trị chuỗi và khóa đối tượng phải được đặt trong dấu ngoặc kép. Dấu ngoặc đơn không phải là dấu phân cách chuỗi JSON hợp lệ. Đây là một trong những lỗi phổ biến nhất của nhà phát triển chuyển từ JavaScript, nơi dấu ngoặc đơn và ngoặc kép có thể dùng thay thế cho nhau.
// Invalid - single quotes
{'name': 'Alice', 'age': 30}
// Valid - double quotes
{"name": "Alice", "age": 30}Khóa Đối Tượng Phải Được Đặt Trong Dấu Ngoặc Kép
Không giống JavaScript object literal, JSON yêu cầu tất cả khóa đối tượng phải được đặt trong dấu ngoặc kép. Khóa không được đặt trong dấu ngoặc kép là lỗi cú pháp.
// Invalid - unquoted keys
{name: "Alice", age: 30}
// Valid - quoted keys
{"name": "Alice", "age": 30}Không Được Có Dấu Phẩy Cuối Cùng
JSON không cho phép dấu phẩy sau mục cuối cùng trong đối tượng hoặc mảng. Đây là một lỗi phổ biến khác của nhà phát triển JavaScript, nơi dấu phẩy cuối cùng được cho phép (và thậm chí được khuyến khích trong một số style guide).
// Invalid - trailing comma
{
"name": "Alice",
"age": 30,
}
// Valid - no trailing comma
{
"name": "Alice",
"age": 30
}Không Hỗ Trợ Chú Thích
JSON không hỗ trợ chú thích. Cả // chú thích một dòng và /* */ chú thích nhiều dòng đều không hợp lệ trong JSON. Nếu bạn cần bao gồm tài liệu, hãy cân nhắc sử dụng file tài liệu riêng hoặc định dạng JSONC (JSON with Comments) được một số công cụ hỗ trợ.
Kiểu Giá Trị Nghiêm Ngặt
Giá trị JSON phải là một trong sáu kiểu được hỗ trợ. undefined, NaN, Infinity và -Infinity không phải là giá trị JSON hợp lệ. Bao gồm bất kỳ giá trị nào trong số này sẽ gây ra lỗi phân tích cú pháp hoặc tạo ra JSON không chuẩn mà nhiều trình phân tích sẽ từ chối.
Lỗi Định Dạng Phổ Biến
Hai chế độ định dạng chính của JSON phục vụ các mục đích khác nhau, và sử dụng đúng định dạng trong đúng ngữ cảnh là quan trọng.
- Thụt lề không nhất quán: Trộn tab và khoảng trắng, hoặc sử dụng độ sâu thụt lề khác nhau, làm cho JSON khó đọc hơn trong code review và diff. Chuẩn hóa sử dụng thụt lề 2 khoảng trắng, đây là quy ước phổ biến nhất.
- Cấu trúc lồng sâu: JSON lồng quá 4-5 cấp trở nên khó đọc và gỡ lỗi. Cân nhắc làm phẳng cấu trúc hoặc chia thành các tài liệu riêng biệt.
- Thứ tự khóa không nhất quán: Mặc dù đối tượng JSON về mặt kỹ thuật là không có thứ tự, duy trì thứ tự khóa nhất quán (như theo thứ tự bảng chữ cái hoặc theo mức độ quan trọng) làm cho diff có ý nghĩa hơn và giảm xung đột merge trong version control.
- Dòng quá dài: Mảng có nhiều mục trên một dòng khó duyệt. Chia mảng dài thành nhiều dòng, mỗi dòng một mục để cải thiện khả năng đọc.
- Xử lý null thiếu hoặc không nhất quán: Quyết định loại bỏ hoàn toàn giá trị null hay bao gồm chúng một cách rõ ràng, và áp dụng nhất quán trên toàn bộ API.
Pretty Print và Minify
Pretty print thêm thụt lề và xuống dòng để làm cho JSON có thể đọc được đối với con người. Điều này rất quan trọng trong quá trình phát triển, gỡ lỗi và viết tài liệu. Hầu hết các trình định dạng JSON sử dụng thụt lề 2 khoảng trắng theo mặc định.
JSON Pretty Print
Minify loại bỏ tất cả khoảng trắng không cần thiết, tạo ra JSON hợp lệ nhỏ nhất có thể. Điều này rất quan trọng cho API production nơi mỗi byte đều quan trọng.
{
"users": [
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
},
{
"id": 2,
"name": "Bob",
"email": "bob@example.com"
}
]
}JSON Minified
Mặc dù xác thực cú pháp JSON kiểm tra xem tài liệu có đúng định dạng không, nó không xác thực dữ liệu có cấu trúc, kiểu hoặc giá trị mong đợi không. JSON Schema lấp đầy khoảng trống này bằng cách cung cấp từ vựng để mô tả hình dạng mong đợi của dữ liệu JSON.
{"users":[{"id":1,"name":"Alice","email":"alice@example.com"},{"id":2,"name":"Bob","email":"bob@example.com"}]}Khi Nào Sử Dụng Loại Nào
| Ngữ Cảnh | Định Dạng | Lý Do |
|---|---|---|
| Phát triển và gỡ lỗi | Pretty print | Khả năng đọc và quét nhanh |
| File cấu hình | Pretty print | Con người cần đọc và chỉnh sửa các file này |
| Phản hồi API production | Minified | Tải nhỏ hơn, truyền tải nhanh hơn |
| File log | Minified (mỗi dòng một đối tượng) | Lưu trữ gọn, dễ grep |
| Version control | Pretty print | Diff có ý nghĩa và ít xung đột merge hơn |
Xác Thực JSON Schema
JSON Schema là một tài liệu JSON mô tả cấu trúc của các tài liệu JSON khác. Nó cho phép bạn chỉ định các trường bắt buộc, kiểu mong đợi, phạm vi giá trị, mẫu chuỗi và cấu trúc đối tượng lồng nhau. Một tài liệu JSON phù hợp với Schema được gọi là instance hợp lệ.
JSON Schema Là Gì?
Bạn nên sử dụng xác thực JSON Schema bất cứ khi nào nhận JSON từ nguồn bên ngoài: body yêu cầu API, file cấu hình, nhập dữ liệu và tải message queue. Xác thực Schema có thể phát hiện lỗi sớm, cung cấp thông báo lỗi rõ ràng và hoạt động như tài liệu sống cho định dạng dữ liệu. Các thư viện như Ajv (JavaScript), jsonschema (Python) và json-schema-validator (Java) giúp dễ dàng tích hợp xác thực Schema vào bất kỳ ứng dụng nào.
Các Tính Năng Schema Phổ Biến
- Kiểm tra kiểu: Đảm bảo trường là chuỗi, số, boolean, đối tượng hoặc mảng.
- Trường bắt buộc: Chỉ định thuộc tính nào phải tồn tại.
- Xác thực chuỗi: Thực thi mẫu (regex), độ dài tối thiểu/tối đa và định dạng (email, datetime, URI).
- Xác thực số: Đặt giá trị tối thiểu, tối đa, ranh giới loại trừ và ràng buộc bội số.
- Xác thực mảng: Kiểm soát kiểu mục, số lượng mục tối thiểu/tối đa và tính duy nhất.
- Kết hợp: Sử dụng allOf, anyOf, oneOf và not cho logic xác thực phức tạp.
Khi Nào Sử Dụng Xác Thực Schema
Kích thước của tài liệu JSON ảnh hưởng trực tiếp đến hiệu suất ứng dụng theo nhiều cách: thời gian truyền tải mạng, thời gian phân tích cú pháp và tiêu thụ bộ nhớ. Hiểu những tác động này giúp bạn đưa ra quyết định sáng suốt về định dạng và cấu trúc JSON.
Tác Động Hiệu Suất Của Kích Thước JSON
Mỗi byte JSON phải di chuyển qua mạng từ server đến client. Sự khác biệt giữa 10KB và 100KB có vẻ nhỏ trên kết nối nhanh, nhưng trên mạng di động hoặc đối với người dùng ở khu vực internet chậm, tác động là đáng kể. Nghiên cứu cho thấy mỗi 100ms thời gian tải tăng thêm làm giảm tỷ lệ chuyển đổi khoảng 1%. Nén thường giảm kích thước JSON 30-50%, nén gzip giảm thêm 70-85%. Luôn bật nén gzip hoặc Brotli cho phản hồi API JSON.
Truyền Tải Mạng
Phân tích cú pháp JSON tốn kém đáng ngạc nhiên. Đối với tài liệu lớn (trên 1MB), việc phân tích trên thiết bị di động có thể mất hàng trăm mili giây. Chi phí gần như tuyến tính với kích thước tài liệu. Các chiến lược chính để giảm chi phí phân tích cú pháp bao gồm: chỉ gửi dữ liệu client cần (lọc trường), phân trang cho tập kết quả lớn và sử dụng định dạng tuần tự hóa hiệu quả hơn như Protocol Buffers hoặc MessagePack cho giao tiếp giữa các dịch vụ nội bộ không cần khả năng đọc của con người.
Hiệu Suất Phân Tích Cú Pháp
JSON đã phân tích thường tiêu thụ bộ nhớ gấp 3-10 lần so với dạng tuần tự hóa, vì mỗi giá trị trở thành một đối tượng riêng biệt với phân bổ bộ nhớ riêng. Chuỗi JSON 1MB sau khi phân tích có thể sử dụng 5-10MB RAM. Đối với ứng dụng JavaScript chạy trong trình duyệt có bộ nhớ hạn chế, điều này có thể dẫn đến suy giảm hiệu suất hoặc crash trên thiết bị cấp thấp.
Sử Dụng Bộ Nhớ
JSON Lines (JSONL hoặc NDJSON) là định dạng liên quan giải quyết một hạn chế chính của JSON: yêu cầu phân tích toàn bộ tài liệu như một đơn vị duy nhất. Trong JSONL, mỗi dòng của file là một đối tượng JSON hoàn chỉnh, độc lập.
JSON và JSONL
File JSON lớn (trên 10MB) đặt ra thách thức đặc biệt đòi hỏi xử lý đặc biệt. Phương pháp phân tích tiêu chuẩn có thể thất bại hoặc hoạt động kém ở quy mô này.
Khi Nào Sử Dụng JSONL
- File log: Mỗi bản ghi log là đối tượng JSON độc lập trên một dòng riêng. Bạn có thể thêm bản ghi mới mà không cần sửa đổi dữ liệu hiện có và có thể đọc độc lập bất kỳ dòng nào.
- Luồng dữ liệu: Xử lý bản ghi khi đến mà không cần đợi tập dữ liệu đầy đủ. Mỗi dòng là một thông điệp hoàn chỉnh.
- Tập dữ liệu lớn: Phân tích và xử lý từng bản ghi một mà không cần tải toàn bộ file vào bộ nhớ. Điều này quan trọng cho tập dữ liệu vượt quá RAM khả dụng.
- Xử lý song song: Chia file JSONL tại ranh giới dòng và phân phối các khối cho các worker khác nhau. Điều này không thể với JSON tiêu chuẩn vì chia tại vị trí byte tùy ý sẽ phá vỡ cấu trúc.
Khi Nào Sử Dụng JSON Tiêu Chuẩn
- Phản hồi API: JSON tiêu chuẩn là định dạng mong đợi cho REST API. Bọc kết quả trong mảng hoặc đối tượng là thông thường và được mong đợi.
- File cấu hình: Cấu hình thường cần được tải một lần, vì vậy lợi thế streaming của JSONL không liên quan.
- Cấu trúc dữ liệu lồng nhau: Nếu dữ liệu của bạn có cấu trúc lồng phức tạp không thể dễ dàng làm phẳng thành các bản ghi riêng biệt, JSON tiêu chuẩn tự nhiên hơn.
Xử Lý File JSON Lớn
Trình phân tích streaming (hoặc kiểu SAX) xử lý JSON tăng dần mà không cần tải toàn bộ tài liệu vào bộ nhớ. Chúng phát ra sự kiện khi gặp các phần tử cấu trúc như bắt đầu đối tượng, cặp khóa-giá trị và mục mảng. Phương pháp này sử dụng bộ nhớ không đổi bất kể kích thước file. Các thư viện như oboe.js (JavaScript), ijson (Python) và Jackson Streaming API (Java) cung cấp phân tích JSON streaming.
Trình Phân Tích Cú Pháp Streaming
Mặc dù JSON là định dạng dữ liệu và bản thân không không an toàn, cách ứng dụng xử lý JSON có thể tạo ra lỗ hổng. Hiểu những rủi ro này là điều cần thiết để xây dựng hệ thống an toàn.
Mẹo Thực Tế Cho File Lớn
- Chuyển đổi sang JSONL trước: Nếu bạn có một mảng JSON lớn, chuyển đổi nó sang JSONL (mỗi dòng một đối tượng) cho phép xử lý từng dòng bằng các công cụ tiêu chuẩn như grep, awk và jq.
- Sử dụng công cụ dòng lệnh: jq là công cụ tiêu chuẩn để xử lý JSON từ dòng lệnh. Nó có thể xử lý file lớn hiệu quả và hỗ trợ chế độ streaming cho đầu vào siêu lớn.
- Chia và song song hóa: Chia file JSONL lớn thành các khối nhỏ hơn và xử lý song song. Mỗi khối có thể được xử lý độc lập vì mỗi dòng là độc lập.
- Tránh tải vào bộ nhớ: Không bao giờ sử dụng JSON.parse() cho file lớn hơn bộ nhớ khả dụng. Sử dụng trình phân tích streaming hoặc xử lý file từng dòng.
- Lưu trữ nén: File JSON lớn nén rất tốt với gzip (thường giảm 80-90%). Giữ bản sao nén để lưu trữ, giải nén ngay trong khi xử lý.
Lưu Ý Bảo Mật JSON
Quy tắc bảo mật quan trọng nhất: không bao giờ sử dụng hàm eval() của JavaScript để phân tích JSON. eval() thực thi mã JavaScript tùy ý, có nghĩa là tải JSON độc hại có thể chạy code trên máy người dùng. Luôn sử dụng JSON.parse(), chỉ phân tích JSON hợp lệ và từ chối mọi code thực thi. Điều này là không thể thương lượng.
Không Bao Giờ Dùng eval() Để Phân Tích JSON
JSONP (JSON with Padding) là kỹ thuật được sử dụng trước khi CORS được hỗ trợ rộng rãi để vượt qua hạn chế same-origin policy. Nó hoạt động bằng cách bọc dữ liệu JSON trong một lời gọi hàm được thực thi dưới dạng script. Điều này vốn nguy hiểm vì nó thực thi JavaScript tùy ý từ server bên thứ ba. Nếu bạn kiểm soát cả client và server, hãy sử dụng CORS thay vì JSONP. JSONP nên được coi là công nghệ cũ và nên tránh trong ứng dụng mới.
JSONP và Rủi Ro Cross-Domain
Khi merge hoặc deep copy đối tượng JSON trong JavaScript, hãy cảnh giác với các khóa như __proto__, constructor và prototype. Nếu JSON do người dùng cung cấp được merge đệ quy vào đối tượng hiện có mà không làm sạch các khóa này, nó có thể sửa đổi prototype của tất cả đối tượng trong ứng dụng, dẫn đến leo thang đặc quyền hoặc từ chối dịch vụ. Luôn làm sạch khóa đối tượng trước khi merge.
Prototype Pollution
Tài liệu JSON được xây dựng cẩn thận với độ sâu lồng ghép cực cao (hàng nghìn cấp) có thể gây ra lỗi stack overflow trong trình phân tích đệ quy. Giảm thiểu bằng cách đặt độ sâu lồng tối đa trong trình phân tích. Hầu hết trình phân tích JSON production cho phép bạn cấu hình giới hạn này.
Từ Chối Dịch Vụ Qua Lồng Ghép Sâu
Không bao giờ tin tưởng dữ liệu JSON từ nguồn bên ngoài. Luôn xác thực cấu trúc, kiểu và phạm vi giá trị của JSON đến trước khi sử dụng. Xác thực JSON Schema là phương pháp mạnh mẽ nhất, nhưng ngay cả kiểm tra đơn giản về trường bắt buộc và xác nhận kiểu cũng cung cấp bảo vệ đáng kể chống lại đầu vào không đúng định dạng hoặc độc hại.
Xác Thực Đầu Vào
Cần định dạng, xác thực hoặc nén JSON? Hãy dùng thử công cụ JSON trực tuyến miễn phí của chúng tôi. Tất cả xử lý diễn ra trong trình duyệt của bạn, đảm bảo tốc độ và quyền riêng tư tối đa.
JSON pretty print chứa khoảng trắng (thụt lề, xuống dòng) để con người đọc, trong khi JSON minified loại bỏ tất cả khoảng trắng không cần thiết để giảm thiểu kích thước file. Sử dụng JSON pretty print trong quá trình phát triển và gỡ lỗi, sử dụng JSON minified trong môi trường production để có tải mạng nhỏ hơn và phân tích nhanh hơn.
Công Cụ Định Dạng JSONTrình Xác Thực JSONCâu Hỏi Thường Gặp
Sự khác biệt giữa JSON pretty print và minified là gì?
Các lỗi phổ biến nhất là: dấu phẩy cuối cùng sau mục cuối của đối tượng hoặc mảng, sử dụng dấu ngoặc đơn thay vì dấu ngoặc kép cho chuỗi, thêm chú thích (JSON không hỗ trợ chú thích), sử dụng khóa đối tượng không đặt trong dấu ngoặc kép và bao gồm các giá trị không hợp lệ như undefined hoặc NaN.
Các lỗi định dạng JSON phổ biến nhất là gì?
Sử dụng JSONL (JSON Lines) khi bạn cần xử lý bản ghi tăng dần, như file log, luồng dữ liệu hoặc tập dữ liệu lớn không thể nạp vào bộ nhớ. Mỗi dòng là một đối tượng JSON hoàn chỉnh, vì vậy bạn có thể đọc và phân tích từng dòng một mà không cần tải toàn bộ file. JSON tiêu chuẩn yêu cầu phân tích toàn bộ tài liệu trước khi truy cập bất kỳ dữ liệu nào.
Khi nào nên sử dụng JSONL thay vì JSON?
Sử dụng JSON Schema để định nghĩa cấu trúc mong đợi của dữ liệu JSON, sau đó sử dụng các thư viện như Ajv (JavaScript), jsonschema (Python) hoặc trình xác thực trực tuyến để xác thực instance theo Schema đó. JSON Schema cho phép bạn chỉ định trường bắt buộc, kiểu, phạm vi giá trị, mẫu chuỗi và cấu trúc đối tượng lồng nhau.
Làm thế nào để xác thực JSON theo Schema?
Bản thân JSON là định dạng dữ liệu, không an toàn cũng không không an toàn. Tuy nhiên, cách bạn phân tích và sử dụng JSON có thể tạo ra lỗ hổng. Rủi ro chính là sử dụng eval() để phân tích JSON (không bao giờ làm điều này — luôn sử dụng JSON.parse()). Ngoài ra, hãy cảnh giác với JSONP, có thể vượt qua same-origin policy. Luôn xác thực và làm sạch dữ liệu JSON từ nguồn không tin cậy trước khi sử dụng.
JSON có an toàn cho trao đổi dữ liệu không?
JSON itself is a data format and is neither secure nor insecure. However, how you parse and use JSON can introduce vulnerabilities. The main risk is using eval() to parse JSON (never do this �?always use JSON.parse()). Also be cautious with JSONP, which can bypass same-origin policies. Always validate and sanitize JSON data from untrusted sources before using it.