Cách Douglas Crockford phổ biến JSON và lý do nó trở thành định dạng mặc định cho ứng dụng web và API — kèm mẹo thực tiễn để dùng JSON hiệu quả ngày nay.

JSON (JavaScript Object Notation) là một cách nhẹ để biểu diễn dữ liệu dưới dạng văn bản thuần, sử dụng các cặp khóa–giá trị và danh sách.
Nếu bạn xây dựng ứng dụng web — ngay cả khi bạn không nghĩ nhiều về “định dạng dữ liệu” — JSON có lẽ đã là thứ kết dính sản phẩm của bạn. Nó là cách frontend yêu cầu dữ liệu, backend trả lời, ứng dụng di động đồng bộ trạng thái, và dịch vụ bên thứ ba gửi sự kiện. Khi JSON rõ ràng và nhất quán, nhóm phát triển ra tính năng nhanh hơn; khi nó lộn xộn, mọi tính năng đều kéo dài vì ai cũng tranh cãi về ý nghĩa của dữ liệu.
Đây là một đối tượng JSON nhỏ mà bạn có thể đọc trong chớp mắt:
{
"userId": 42,
"name": "Sam",
"isPro": true,
"tags": ["beta", "newsletter"]
}
Ngay cả khi không có ngữ cảnh kỹ thuật, bạn thường suy ra được: một người dùng có ID, tên, cờ trạng thái và danh sách tag.
Bạn sẽ học được:
Mục tiêu đơn giản: giúp bạn hiểu không chỉ JSON là gì, mà còn tại sao gần như mọi ứng dụng “nói” bằng JSON — và cách tránh những sai lầm thường gặp mà các nhóm vẫn lặp lại.
Douglas Crockford không “phát minh” mọi ý tưởng đằng sau JSON, nhưng ông đã làm điều quan trọng không kém: làm cho một mẫu đơn giản, khả thi trở nên rõ ràng, đặt tên cho nó và đẩy nó vào dòng chính.
Trong những ngày đầu của ứng dụng web, các nhóm phải xoay xở với các lựa chọn vụng về để chuyển dữ liệu giữa trình duyệt và máy chủ. XML phổ biến nhưng dài dòng. Các định dạng tùy chỉnh dựa trên dấu phân cách thì gọn nhưng mong manh. JavaScript về lý thuyết có thể đánh giá dữ liệu như mã, nhưng điều đó làm nhòe ranh giới giữa “dữ liệu” và “script thực thi”, dẫn đến lỗi và vấn đề bảo mật.
Crockford nhìn thấy một con đường rõ ràng hơn: dùng một tập con nhỏ của cú pháp literal JavaScript để biểu diễn dữ liệu thuần đáng tin cậy — object, array, string, number, boolean, và null — không thêm các tính năng thừa.
Một trong những đóng góp lớn nhất của Crockford là thuộc về mặt xã hội, không phải kỹ thuật: ông gọi nó là JSON (JavaScript Object Notation) và xuất bản tài liệu rõ ràng tại json.org. Điều đó cung cấp cho các nhóm một từ vựng chung (“chúng ta sẽ gửi JSON”) và một tham chiếu đủ ngắn để đọc và đủ nghiêm ngặt để triển khai.
Ông cũng quảng bá JSON như một định dạng dữ liệu độc lập với JavaScript: nhiều ngôn ngữ có thể phân tích và tạo JSON, và nó ánh xạ tự nhiên tới các cấu trúc dữ liệu phổ biến.
Việc áp dụng tăng tốc khi các nhóm cảm thấy an toàn khi đặt cược vào một định dạng dài hạn. JSON dần dần có được vị thế “đáng tin cậy” đó qua các cột mốc rộng rãi biết đến:
Sự vận động của Crockford, kết hợp với các tiêu chuẩn này và hệ sinh thái parser ngày càng lớn, đã giúp JSON chuyển từ một quy ước tiện lợi thành cách mặc định để các ứng dụng giao tiếp — đặc biệt qua HTTP APIs (được đề cập sau trong /blog/json-and-apis).
Trước khi JSON trở thành cách mặc định để chuyển dữ liệu, web dựa vào sự pha trộn các định dạng quá nặng, quá không nhất quán, hoặc quá tùy chỉnh để mở rộng qua các nhóm.
XML là lựa chọn “chuẩn” lớn. Nó chạy được trên nhiều ngôn ngữ, có tooling, và có thể biểu diễn cấu trúc lồng nhau. Nó cũng mang theo nhiều nghi thức.
Cùng lúc đó, nhiều app gửi dữ liệu dưới dạng query string tùy chỉnh (đặc biệt trong các yêu cầu kiểu AJAX ban đầu): cặp key/value nhồi vào URL hoặc thân POST. Những app khác sáng tạo các định dạng văn bản tự tạo—một danh sách CSV ở đây, một khối phân cách bằng dấu gạch đứng ở kia—thường với quy tắc escape viết tay mà chỉ một người dev hiểu.
Vấn đề chung không phải là lý thuyết:
Hầu hết app không cần định dạng có thể biểu diễn mọi cấu trúc tài liệu có thể. Họ cần một cách dự đoán để gửi object, array, string, number, và boolean — nhanh, nhất quán và ít chỗ để hiểu nhầm. Các định dạng đơn giản làm giảm số quyết định (và lỗi) mà nhóm phải đưa ra cho mỗi endpoint.
<user>
<id>42</id>
<name>Ada</name>
<isActive>true</isActive>
</user>
{
"id": 42,
"name": "Ada",
"isActive": true
}
Cả hai diễn đạt cùng ý, nhưng JSON dễ đọc hơn, dễ sinh hơn và gần hơn với cách hầu hết ứng dụng mô hình hóa dữ liệu trong bộ nhớ.
Sức bền của JSON không phải ngẫu nhiên. Nó thành công vì nó cố ý nhỏ: vừa đủ cấu trúc để biểu diễn dữ liệu ứng dụng thật mà không mở cửa cho vô số biến thể.
JSON cung cấp một bộ công cụ tối giản phù hợp với cách hầu hết app nghĩ về dữ liệu:
name, email)true/falseChỉ có vậy. Không có dates, không có comments, không có kiểu số tùy chỉnh, không có tham chiếu. Sự đơn giản này khiến JSON dễ triển khai trên nhiều ngôn ngữ và nền tảng.
JSON đủ đọc để con người quét nhanh trong log và phản hồi API, đồng thời đủ dễ để máy parse nhanh. Nó tránh nghi thức thừa mà vẫn giữ các ký tự phân cách rõ ràng ({}, [], :) để parser có thể nhanh và đáng tin cậy.
Đổi lại: vì JSON quá tối giản, các nhóm phải đồng ý các quy ước cho timestamp, tiền tệ và định danh (ví dụ, dùng chuỗi ISO-8601 cho ngày).
Luật nghiêm ngặt của JSON (chuỗi phải để trong dấu ngoặc kép, không có dấu phẩy thừa, tập các kiểu cố định) giảm sự mơ hồ. Ít mơ hồ nghĩa là ít lỗi “chỉ chạy trên máy tôi” khi hệ thống khác nhau trao đổi dữ liệu.
JSON trông giống cú pháp object của JavaScript, nhưng JSON không phải JavaScript. Nó là một định dạng dữ liệu độc lập với ngôn ngữ, có quy tắc riêng, dùng được từ Python, Java, Go, Ruby và bất kỳ nơi nào cần tuần tự hóa nhất quán và tương tác.
JSON không thắng vì nó nhiều tính năng nhất. Nó thắng vì nó phù hợp với cách ứng dụng web đã được xây dựng: trình duyệt nhiều JavaScript nói chuyện với máy chủ qua các yêu cầu HTTP đơn giản.
Khi trình duyệt chuẩn hóa quanh JavaScript, phía client có cách sẵn có để biểu diễn dữ liệu có cấu trúc: object, array, string, number, boolean, null. JSON phản chiếu gần như chính xác các nguyên thủy đó, nên việc chuyển dữ liệu giữa “cái trình duyệt hiểu” và “cái server gửi” trở nên tự nhiên.
Các app theo kiểu Ajax ban đầu đã đẩy nhanh điều này. Thay vì trả nguyên trang HTML, server có thể trả một payload nhỏ để UI render. Một phản hồi như sau ngay lập tức hữu ích:
{
"user": {"id": 42, "name": "Sam"},
"unreadCount": 3
}
Mặc dù cú pháp JSON trông như JavaScript, nó là trung lập về ngôn ngữ. Ngay khi server và client trong các ngôn ngữ khác cần tương tác với frontend web, thư viện JSON xuất hiện — và nhanh chóng trở thành “trang bị chuẩn”. Parse một chuỗi JSON thành cấu trúc dữ liệu bản địa thường chỉ là một lời gọi hàm, và tạo JSON cũng tương tự đơn giản.
Khi framework, client API, debugger, proxy và công cụ tài liệu giả định JSON, chọn thứ khác tạo ra ma sát. Dev có thể kiểm tra payload trong devtools trình duyệt, copy/paste ví dụ vào test, và dựa vào thư viện trưởng thành để mã hóa, giải mã và xử lý lỗi.
Một phản hồi JSON duy nhất có thể phục vụ UI web, app di động, dịch vụ nội bộ và tích hợp bên thứ ba mà hầu như không cần thay đổi. Tính tương tác đó khiến JSON trở thành lựa chọn an toàn cho các nhóm xây dựng “một backend cho nhiều frontend” — và giúp nó trở thành hợp đồng mặc định giữa client và server.
JSON không thắng vì nó hoa mỹ — nó phù hợp gọn với cách web đã hoạt động. HTTP xây xung quanh gửi yêu cầu và nhận phản hồi, và JSON là cách dễ dàng, dự đoán để biểu diễn thân (body) của phản hồi (hoặc yêu cầu) như dữ liệu có cấu trúc.
Một yêu cầu API thường gồm method và URL (ví dụ, GET /users?limit=20). Server trả mã trạng thái (như 200 hoặc 404), header và một thân tùy chọn.
Khi thân là JSON, một header quan trọng là:
Content-Type: application/jsonHeader đó báo cho client cách hiểu các byte nhận được. Khi gửi vào (client → server), gửi Content-Type: application/json nói “tôi đang đăng JSON”, và server có thể parse nó nhất quán.
JSON đặc biệt phù hợp cho các mẫu lặp lại xuất hiện trên nhiều API.
Phân trang thường đóng gói một danh sách với metadata:
{
"data": [{"id": 1, "name": "A"}],
"pagination": {"limit": 20, "offset": 0, "total": 153}
}
Lọc và sắp xếp thường xảy ra trong query string URL, trong khi kết quả vẫn là mảng JSON (hoặc trường data). Ví dụ: GET /orders?status=paid&sort=-created_at.
Phản hồi lỗi hưởng lợi từ một hình dạng chuẩn để client có thể hiện thông báo và xử lý retry:
{
"error": {
"code": "invalid_request",
"message": "limit must be between 1 and 100",
"details": {"field": "limit"}
}
}
Sự phù hợp thực tế là đơn giản: HTTP cung cấp giao vận và ý nghĩa (verbs, status codes, caching), còn JSON cung cấp cấu trúc nhẹ, dễ đọc cho chính dữ liệu.
Khi người ta so sánh JSON và XML, họ thường thực ra đang so “dữ liệu cho ứng dụng” với “tài liệu”. Cả hai định dạng đều có thể biểu diễn thông tin có cấu trúc, nhưng JSON có xu hướng phù hợp với những gì hầu hết ứng dụng thực sự chuyển: object đơn giản, list, string, number, boolean và null.
XML dài dòng theo thiết kế. Việc lặp lại tag mở và đóng khiến payload lớn hơn và khó quét trong log hay công cụ inspect mạng. JSON thường truyền tải cùng ý nghĩa với ít ký tự hơn và bớt lộn xộn trực quan, điều này hữu ích khi debug và có thể giảm chi phí băng thông ở quy mô.
Đây không chỉ là về thẩm mỹ: payload nhỏ hơn thường có nghĩa truyền nhanh hơn và parser/proxy ít việc hơn.
Hầu hết dữ liệu ứng dụng tự nhiên trông như dictionaries (map key/value) và arrays (danh sách): một user với thuộc tính, một order với dòng hàng, một trang với thành phần. JSON ánh xạ trực tiếp tới mô hình đó và khớp với cấu trúc bản địa trong JavaScript và phần lớn ngôn ngữ hiện đại.
XML có thể biểu diễn cùng cấu trúc, nhưng thường đòi hỏi quy ước: attribute vs element, child element lặp cho danh sách, và thêm quy tắc để biết “cái nào là số” (vì mọi thứ là text trừ khi bạn thêm kiểu).
XML vẫn mạnh cho các tình huống tập trung vào tài liệu: nội dung hỗn hợp (văn bản xen kẽ với markup), quy trình xuất bản, và hệ sinh thái có tooling XML trưởng thành (ví dụ, một số tích hợp doanh nghiệp). Nếu payload của bạn gần hơn với một tài liệu hơn là một đồ thị đối tượng, XML có thể phù hợp.
Nếu mục tiêu chính là trao đổi dữ liệu ứng dụng giữa frontend, backend và API, JSON thường là lựa chọn đơn giản và trực tiếp. Nếu bạn cần markup tài liệu, nội dung hỗn hợp, hoặc tích hợp vào miền nặng XML, XML có thể là công cụ tốt hơn.
JSON trông như “object JavaScript”, nên các nhóm thường nghĩ họ có thể đối xử như JavaScript. Đó là nơi lỗi xuất hiện: JSON nghiêm ngặt hơn, nhỏ hơn, và ít khoan dung.
Một vài vấn đề “chỉ chạy trên máy tôi” xuất hiện lặp lại:
{name: "Ada"} không phải JSON; { "name": "Ada" } mới là JSON.{ "a": 1, } sẽ lỗi ở nhiều parser.// và /* ... */ đều không hợp lệ. Nếu cần ghi chú, để trong tài liệu hoặc dùng một trường riêng (cẩn thận) trong giai đoạn phát triển.Các ràng buộc này có chủ đích: giữ parser đơn giản và nhất quán giữa các ngôn ngữ.
JSON chỉ có một kiểu số: number. Không có integer, decimal hay date tích hợp.
"19.99") để tránh khác biệt làm tròn giữa hệ thống."2025-12-26T10:15:30Z"). Tránh định dạng ngày tùy chỉnh cần đoán.JSON là Unicode, nhưng hệ thống thực tế vẫn vấp phải mã hóa và escape:
" và dấu gạch chéo ngược \\).Luôn parse JSON bằng parser thực tế (JSON.parse hoặc tương đương trong ngôn ngữ của bạn). Tránh cách tiếp cận kiểu eval, dù có vẻ nhanh hơn. Và validate input ở biên—đặc biệt cho API công khai—để trường hoặc kiểu không mong đợi không lọt vào logic nghiệp vụ.
Một payload JSON không chỉ là “dữ liệu đang truyền” — nó là giao diện lâu dài giữa các nhóm, hệ thống và bạn trong tương lai. Sự khác biệt giữa payload tồn tại lâu và payload bị viết lại hàng quý thường là kỷ luật nhàm chán: nhất quán, quản lý thay đổi cẩn thận, và các trường hợp cạnh lường trước được.
Chọn quy tắc đặt tên và giữ chúng:
camelCase hoặc snake_case) và đừng trộn.userId thành id là thay đổi phá vỡ dù ý nghĩa có vẻ “hiển nhiên”."count": 3 vs "count": "3") sẽ gây lỗi khó truy.Bạn có thể tránh phần lớn tranh cãi version bằng cách thay đổi có tính bổ sung:
/v2/...) hoặc đưa tín hiệu version rõ trong header — đừng thay đổi ngầm ý nghĩa.Client xử lý lỗi tốt nhất khi lỗi có cùng hình dạng:
{
"error": {
"code": "INVALID_ARGUMENT",
"message": "email must be a valid address",
"details": { "field": "email" }
}
}
Tài liệu JSON tốt có ví dụ thật — phản hồi thành công và thất bại — với các trường đầy đủ. Giữ ví dụ đồng bộ với hành vi production, và ghi rõ trường nào optional, nullable, hoặc deprecated. Khi ví dụ khớp với phản hồi thực, tích hợp nhanh hơn và ít lỗi hơn.
Nếu bạn dùng workflow vibe-coding để dựng nhanh tính năng, hợp đồng JSON càng quan trọng: lặp nhanh rất tốt cho tới khi client và service drift. Trên Koder.ai, các nhóm thường sinh frontend React cộng backend Go + PostgreSQL rồi lặp thông qua hình dạng API ở chế độ planning trước khi khóa lại. Các tính năng như snapshots và rollback hữu ích khi một thay đổi JSON “nhỏ” hóa ra lại phá vỡ, và xuất mã nguồn giúp giữ hợp đồng trong repo và thực thi bằng test.
JSON dễ sinh, đó vừa là điểm mạnh vừa là cái bẫy. Nếu một service gửi "age": "27" (chuỗi) và service khác mong 27 (số), JSON tự nó sẽ không ngăn. Kết quả thường là loại lỗi tệ nhất: client crash ở production, hoặc giao diện lỗi tinh tế chỉ xuất hiện với một số dữ liệu.
Validation là để bắt dữ liệu sai hoặc không mong đợi trước khi đến tay những người phụ thuộc vào nó — frontend, đối tác, pipeline analytics, hay app di động của bạn.
Các điểm lỗi phổ biến: thiếu field bắt buộc, đổi tên key, sai kiểu, và giá trị “gần đúng” (như ngày ở định dạng khác nhau). Một bước validate nhỏ ở biên API có thể biến outage thành thông báo lỗi rõ ràng.
JSON Schema là cách tiêu chuẩn để mô tả JSON của bạn nên trông ra sao: property bắt buộc, kiểu cho phép, enum, pattern, v.v. Nó hữu dụng nhất khi:
Với schema, bạn có thể validate request ở server, validate response trong test, và sinh tài liệu. Nhiều nhóm kết hợp nó với API docs (thường qua OpenAPI), để hợp đồng rõ ràng thay vì “kiến thức bộ lề”. Nếu bạn đã xuất bản tài liệu dev, liên kết ví dụ schema từ /docs có thể giữ mọi thứ nhất quán.
Không phải đội nào cũng cần tooling schema đầy đủ ngay ngày đầu. Các lựa chọn thực dụng:
Quy tắc hữu ích: bắt đầu bằng ví dụ và test hợp đồng, rồi thêm JSON Schema khi các thay đổi và tích hợp bắt đầu nhân lên.
JSON có vẻ “nhẹ” khi gửi vài trường. Ở quy mô lớn — client di động mạng yếu, API lưu lượng cao, trang nặng analytics — JSON có thể trở thành vấn đề hiệu năng hoặc rủi ro độ tin cậy nếu bạn không định dạng và gửi nó cẩn thận.
Vấn đề phổ biến nhất không phải parse JSON mà là gửi quá nhiều. Phân trang là chiến thắng đơn giản: trả các chunk dự đoán được (ví dụ, limit + cursor) để client không tải hàng nghìn bản ghi một lúc. Với endpoint trả object lồng nhau, cân nhắc partial responses: để client yêu cầu chỉ những trường cần (selected fields hoặc “include” expansions). Điều này tránh overfetching, nơi màn hình chỉ cần name và status nhưng nhận mọi chi tiết lịch sử và cấu hình.
Quy tắc thực dụng: thiết kế phản hồi quanh hành động người dùng (những gì màn hình cần bây giờ), không phải quanh những gì database có thể join.
Nếu API trả phản hồi JSON lớn, nén có thể giảm đáng kể kích thước truyền. Nhiều server có thể gzip hoặc brotli tự động, và hầu hết client xử lý vô hình.
Caching là cần thiết khác. Ở mức cao, nhắm tới:
Điều này giảm download lặp lại và làm mượt đỉnh tải.
Với output rất lớn — export, feed sự kiện, bulk sync — hãy cân nhắc streaming response hoặc parse từng phần để client không phải load toàn bộ document vào bộ nhớ trước khi làm gì đó. Không cần cho hầu hết app, nhưng là lựa chọn hữu ích khi “một blob JSON lớn” bắt đầu timeout.
JSON dễ log, điều đó vừa hữu ích vừa nguy hiểm. Đối xử với log như một sản phẩm:
Làm tốt, bạn debug nhanh hơn trong khi giảm rủi ro rò rỉ dữ liệu.
JSON không “kết thúc” — nó ổn định. Những gì thay đổi giờ là hệ sinh thái xung quanh: editor mạnh hơn, validation tốt hơn, hợp đồng API an toàn hơn, và tooling giúp nhóm tránh thay đổi phá vỡ vô ý.
JSON có khả năng vẫn là định dạng dây truyền mặc định cho hầu hết app web và di động vì nó được hỗ trợ rộng rãi, dễ debug và ánh xạ rõ ràng tới cấu trúc dữ liệu phổ biến.
Sự dịch chuyển lớn nhất là tới API có kiểu: các nhóm vẫn gửi JSON, nhưng định nghĩa nó chính xác hơn bằng công cụ như JSON Schema, OpenAPI, và code generator. Điều đó nghĩa là ít phát hiện “đoán hình dạng”, autocomplete tốt hơn, và phát hiện lỗi sớm hơn — mà không bỏ JSON.
Khi cần gửi hoặc lưu nhiều bản ghi hiệu quả (log, sự kiện analytics, export), một mảng JSON lớn có thể bất tiện. JSON Lines (còn gọi NDJSON) giải quyết bằng cách đặt một object JSON mỗi dòng. Nó stream tốt, xử lý theo dòng, và hợp với tooling dòng lệnh.
Dùng như kiểm tra nhanh trước khi payload sống lâu hơn một sprint:
2025-12-26T10:15:00Z).null và document lựa chọn.Nếu bạn muốn đi sâu, xem các hướng dẫn liên quan trên /blog — đặc biệt các chủ đề như schema validation, versioning API, và thiết kế payload cho tương thích lâu dài.