讲述道格拉斯·克罗克福德如何推广 JSON,以及它为何成为 Web 应用和 API 的默认数据格式,并提供今天有效使用 JSON 的实用建议。

JSON (JavaScript Object Notation) 是一种用简洁文本表示数据的轻量方式,使用键–值对和列表。
如果你在构建 Web 应用——即使你不怎么考虑“数据格式”——JSON 很可能已经是把产品连接在一起的粘合剂。它是前端请求数据的方式、后端响应的方式、移动应用同步状态的方式,以及第三方服务发送事件的方式。当 JSON 清晰且一致时,团队交付更快;当它混乱时,每个功能都需要更久,因为大家在争论数据“是什么意思”。
下面是一个可以一目了然的小 JSON 对象:
{
"userId": 42,
"name": "Sam",
"isPro": true,
"tags": ["beta", "newsletter"]
}
即使没有技术背景,你通常也能推断出正在发生的事:一个用户有 ID、姓名、状态标志和标签列表。
你将了解:
目标很简单:帮助你理解不仅仅是 JSON 是什么,还有为什么几乎每个应用都“会说”它——以及如何避免团队反复犯的常见错误。
道格拉斯·克罗克福德并没有把 JSON 的每个概念都“发明”出来,但他做了一件同等重要的事:把一个简单且可行的模式公开、命名它,并推动其进入主流。
在 Web 应用的早期,团队在浏览器与服务器间移动数据时,要在各种尴尬的选项间抉择。XML 很常见,但冗长;基于自定义分隔符的格式紧凑但脆弱;JavaScript 技术上可以把数据当作代码来求值,但那模糊了“数据”和“可执行脚本”之间的界限,容易引发 bug 与安全问题。
克罗克福德看到了更清晰的路径:使用 JavaScript 字面量语法的一个小子集来可靠地表示纯数据——对象、数组、字符串、数字、布尔与 null——而去掉多余特性。
克罗克福德最大的贡献之一是社会性的而非纯技术性的:他给它命名为 JSON(JavaScript Object Notation),并在 json.org 发布了清晰文档。这为团队提供了共同的词汇(“我们会发送 JSON”)和一个既短又足够严格以供实现的参考页面。
他还推广把 JSON 视为独立于 JavaScript 的数据格式:很多语言都能解析与生成它,并且它自然映射到常见的数据结构。
当团队觉得可以长期押注某个格式时,采用会加速。JSON 通过一些广为人知的里程碑逐步获得这种“可靠赌注”地位:
克罗克福德的推动,加上这些标准和日渐丰厚的解析器生态,帮助 JSON 从一种方便的约定变成了应用之间通信的默认方式——尤其是在 HTTP API 之间(后文并在 /blog/json-and-apis 有更多介绍)。
在 JSON 成为默认传输方式之前,Web 依赖的是一组要么过于沉重、要么不一致、要么高度定制的格式,难以在团队间扩展。
XML 是主要的“标准”选择:跨语言、工具成熟、能表示嵌套结构,但带来很多样板。
同时,许多应用把数据当作 自定义查询字符串 传递(尤其在早期 AJAX 请求中):键/值塞进 URL 或 POST 正文。也有应用发明了临时文本格式——此处逗号分隔、彼处管道分隔——通常伴随着只有一个开发者懂得的手工转义规则。
这些问题并不是理论性的:
大多数应用并不需要能表达每种文档结构的格式。它们需要一种可预测的方式来快速、一致地传输对象、数组、字符串、数字与布尔值——并尽量减少可解释的空间。更简单的格式能降低每个端点需要做出的决策(以及犯错的机会)。
\u003cuser\u003e
\u003cid\u003e42\u003c/id\u003e
\u003cname\u003eAda\u003c/name\u003e
\u003cisActive\u003etrue\u003c/isActive\u003e
\u003c/user\u003e
{
"id": 42,
"name": "Ada",
"isActive": true
}
两者表达同一含义,但 JSON 更易扫描、更易生成,并且更接近大多数应用在内存中对数据的建模方式。
JSON 能够长期存在并非偶然。它之所以成功,是因为它被刻意做得很小:仅提供足够去表示真实应用数据的结构,而不允许无限变化。
JSON 提供的工具集合与大多数应用的数据观念直接对应:
name、email)true / false仅此而已。没有内建日期、没有注释、没有自定义数值类型、没有引用。正因如此,JSON 更容易在不同语言与平台间实现。
JSON 对人足够可读,便于在日志与 API 响应中快速浏览,同时对机器来说也易于解析。它避免了不必要的样板,但保留了清晰分隔符({}、[]、:),使解析器能快速且可靠地工作。
代价是:由于 JSON 极简,团队必须就时间戳、货币和标识符等约定达成一致(例如采用 ISO-8601 表示日期)。
JSON 的严格规则(字符串必须双引号、禁止尾随逗号、类型集合固定)减少了模棱两可。更少的歧义意味着不同系统交换数据时更少出现“在我机器上能跑”的问题。
JSON 看起来像 JavaScript 对象语法,但 JSON 不是 JavaScript。它是一种与语言无关的数据格式,Python、Java、Go、Ruby 等语言都能使用它进行一致的序列化与互操作。
JSON 并非因为功能最丰富而胜出,而是因为它契合 Web 应用的构建方式:浏览器端大量使用 JavaScript,通过简单的 HTTP 请求与服务器通信。
一旦浏览器标准化使用 JavaScript,客户端就有了内建的结构化数据表示:对象、数组、字符串、数字、布尔与 null。JSON 与这些原语高度对应,因此在“浏览器能理解的东西”和“服务器发送的东西”之间传输数据显得很自然。
早期 Ajax 风格的应用加速了这一点。服务器无需返回完整的 HTML 页面,而可以返回一个小负载供 UI 渲染。例如:
{
"user": {"id": 42, "name": "Sam"},
"unreadCount": 3
}
尽管 JSON 语法像 JavaScript,但它是语言中立的。只要服务器与客户端(用其它语言实现)需要与 Web 前端互操作,JSON 库就会出现并且迅速成为“标配”。把 JSON 字符串解析为本地数据结构通常只需一次函数调用,生成 JSON 也同样简单。
当框架、API 客户端、调试器、代理与文档工具都假定使用 JSON,选择其他格式会带来摩擦。开发者可以在浏览器开发者工具中检查负载、把示例粘贴到测试中,并依赖成熟的库来编码、解码与处理错误。
一份 JSON 响应可以同时服务 Web UI、移动端、内部服务与第三方集成,几乎无需改动。这种互操作性让 JSON 成为“一个后端,多前端”场景的安全选择,也促成了它作为客户端与服务器之间默认契约的地位。
JSON 胜出的原因并非华丽,而是它与 Web 的工作方式无间契合。HTTP 围绕请求与响应构建,而 JSON 是表示响应(或请求)主体的轻量且可预测的数据格式。
一次 API 请求通常包含方法和 URL(例如 GET /users?limit=20)。服务器用状态码(如 200 或 404)、头部与可选主体回复。
当主体是 JSON 时,一个关键头部是:
Content-Type: application/json这个头告知客户端如何解释接收到的字节。在客户端→服务器 的请求中发送 Content-Type: application/json 表示“我在提交 JSON”,服务器即可一致地解析它。
JSON 非常适合许多 API 中反复出现的模式。
分页 通常用元数据包裹列表:
{
"data": [{"id": 1, "name": "A"}],
"pagination": {"limit": 20, "offset": 0, "total": 153}
}
过滤与排序 通常出现在 URL 查询串中,而结果仍是 JSON 数组或 data 字段。例如:GET /orders?status=paid\u0026sort=-created_at。
错误响应 受益于统一的形状,便于客户端展示信息并处理重试:
{
"error": {
"code": "invalid_request",
"message": "limit must be between 1 and 100",
"details": {"field": "limit"}
}
}
实务上的匹配很简单:HTTP 提供传输与语义(动词、状态码、缓存),而 JSON 提供轻量且可读的数据结构。
当人们把 JSON 和 XML 做比较时,实际上常是在比较“应用数据”与“文档”。两者都能表示结构化信息,但 JSON 更符合大多数应用实际在移动的数据:简单对象、列表、字符串、数字、布尔与 null。
XML 本就冗长。重复的开闭标签让负载更大,也更难在日志或网络检查器中浏览。JSON 通常用更少字符与更少视觉杂乱来传达相同含义,这有助于调试并在大规模下减少带宽成本。
这不仅是审美问题:更小的负载通常意味着更快传输和更少的解析与代理负担。
大多数应用数据天生像字典(键/值)和数组(列表):一个用户有属性、一个订单有行项、一个页面有组件。JSON 直接映射至这种心智模型,并与 JavaScript 及大多数现代语言的原生数据结构匹配。
XML 可以表示相同结构,但通常需要额外约定:属性 vs 元素、用重复子元素表示列表、以及关于“什么算数字”的额外规则(因为一切都作为文本,除非在上层加类型)。
XML 在文档为中心的场景中仍然很强:混合内容(文本与标记交织)、出版流程,以及某些有成熟 XML 工具链的企业生态。如果你的负载更像文档而不是对象图,XML 仍然可以是更合适的工具。
如果你的主要目标是在前端、后端与 API 间交换应用数据,JSON 通常是更简单、更直接的选择。如果你需要文档标记、混合内容,或必须融入以 XML 为主的领域,XML 可能更合适。
JSON 看起来像“JavaScript 对象”,因此团队常常把它当作 JavaScript 来处理。这正是 bug 的来源之一:JSON 更严格、更精简、更不容忽视细节。
一些“在我机器上能跑”的问题反复出现:
{name: "Ada"} 不是 JSON;{ "name": "Ada" } 才是。{ "a": 1, } 在许多解析器中会失败。// 或 /* ... */ 都是无效的。如果需要注释,请写在文档中或在开发阶段用单独字段(谨慎)记录。这些约束是有意为之:它们让解析器简单且在各语言间表现一致。
JSON 只有一种数字类型:number。没有内建的整数、十进制或日期类型。
"19.99"),以避免跨系统的舍入差异。"2025-12-26T10:15:30Z")。避免使用需要额外猜测的自定义日期格式。JSON 支持 Unicode,但真实系统在编码与转义上仍会出错:
" 与反斜杠 \)。始终使用真实的 JSON 解析器(JSON.parse 或你所用语言的等价函数)来解析 JSON。避免任何 eval 风格的方法,即使看起来“更快”。并在边界处验证输入——尤其是公共 API——以防意外字段或类型渗入业务逻辑。
JSON 负载不仅仅是“在路上的数据”——它是团队、系统与未来的你之间的长期接口。能够长期使用的负载与每个季度都要重写的负载之间的差别,通常来自枯燥但必要的纪律:一致性、谨慎的变更管理与对边界情况的明确约定。
选择一种命名规则并始终如一地使用它:
camelCase 或 snake_case)并避免混用。userId 重命名为 id 是破坏性变更,即使语义看似明显。"count": 3 vs "count": "3")会导致难以追踪的 bug。通过采用增量变化可以避免大多数版本纠纷:
/v2/...)或在头部加入明确版本信号——不要悄然改变语义。当错误具有统一形状时,客户端更容易处理失败:
{
"error": {
"code": "INVALID_ARGUMENT",
"message": "email must be a valid address",
"details": { "field": "email" }
}
}
优秀的 JSON 文档包含真实示例——成功与失败的响应都要包含完整字段。保持示例与生产行为同步,并标注哪些字段是可选、可为 null 或已弃用。当示例与真实响应一致时,集成更快、更少出错。
如果你使用一种快速编码的工作流快速推出功能,JSON 契约变得更加重要:快速迭代很好,但直到客户端和服务发生漂移才发现问题。
在 Koder.ai 上,团队通常生成 React 前端和 Go + PostgreSQL 后端,然后在 planning mode 中先迭代 API 形状再锁定。像 snapshots 和 rollback 的功能在“一个小”JSON 变更变成破坏性变更时很有用,源代码导出 则便于把契约放入仓库并用测试强制执行它们。
JSON 很容易生成,这既是优点也是陷阱。如果一个服务发送 "age": "27"(字符串)而另一个期待 27(数字),JSON 本身不会阻止这种不匹配。结果往往是最糟糕的那类 bug:生产环境中客户端崩溃,或只有在特定数据下才出现的微妙 UI 问题。
验证是为了在坏数据或意外数据到达依赖它的系统之前捕获问题——无论是前端、合作伙伴集成、分析管道还是移动端应用。
常见失败点包括缺失必需字段、键被重命名、类型错误以及“几乎正确”的值(比如不一致的日期格式)。在 API 边界做一步小小的验证,可以把这些问题从宕机变成明确的错误信息。
JSON Schema 是描述 JSON 应有结构的标准方式:必需属性、允许的类型、枚举、模式等。它在以下场景最有用:
有了 schema,你可以在服务器端验证请求、在测试中验证响应并生成文档。很多团队把它与 API 文档(通常通过 OpenAPI)配对,使契约显式而非“部落知识”。如果你已有开发者文档,可以在 /docs 链接 schema 示例以保持一致性。
并非每个团队在第一天就需要完整的 schema 工具链。实用选项包括:
一个有用的规则是:从示例与契约测试开始;当变更与集成增多时再引入 JSON Schema。
当你只发送几字段时,JSON 感觉“轻量”。在大规模场景下——移动客户端在不稳定网络、高流量 API、分析密集页面——如果不谨慎塑形与传输,JSON 可能成为性能或可靠性瓶颈。
最常见的扩展性问题不是解析,而是传输了过多数据。
分页是简单的胜利:返回可预测的块(例如 limit + cursor),防止客户端一次下载数千条记录。对于返回嵌套对象的端点,考虑部分响应:让客户端只请求所需字段(所选字段或 "include" 展开)。这能阻止“过度抓取”,即界面只需 name 和 status,却收到所有历史和配置字段。
一个实用规则:围绕用户动作设计响应(屏幕“现在”需要什么),而不是围绕数据库如何 join。
如果你的 API 返回较大的 JSON,启用压缩能显著减少传输大小。许多服务器可自动做 gzip 或 brotli 压缩,而大多数客户端无需额外代码即可处理。
缓存是另一个重要手段。总体目标是:
这能减少重复下载并缓解流量峰值。
对于非常大的输出——导出、事件流、批量同步——考虑流式响应或增量解析,让客户端不必先把整个文档加载到内存才能开始有用的处理。这对大多数应用不是必需的,但当“一个大 JSON blob”开始超时时,这是有价值的选项。
JSON 易于记录,这既有利也有风险。把日志当作产品接口来处理:
做好这些,你会更快地排查问题,同时降低意外数据泄露的风险。
JSON 不是“完结”的规范——它是稳定的。现在变化的是围绕它的生态:更强的编辑器、更好的验证、更安全的 API 契约以及帮助团队避免意外破坏性改动的工具链。
JSON 很可能继续作为大多数 Web 与移动应用的默认线格式,因为它被广泛支持、易于调试并且与常见数据结构清晰映射。
最大的变化是走向“类型化 API”:团队仍然发送 JSON,但会用 JSON Schema、OpenAPI 和代码生成器等工具更精确地定义它。这意味着更少的“猜测形状”时刻、更好的自动补全与更早的错误发现——而不会放弃 JSON 本身。
当你需要高效地发送或存储大量记录(日志、分析事件、导出)时,一个巨大的 JSON 数组会显得笨重。JSON Lines(也称 NDJSON)通过“每行一个 JSON 对象”解决这个问题。它易于流式处理、可逐行消费,并且与命令行工具兼容良好。
在发布长期存在的负载前,作为快速检查表使用:
2025-12-26T10:15:00Z)。null 并在文档中说明你的选择。如果想深入,可在 /blog 浏览相关指南——特别是关于 schema 验证、API 版本化以及为长期兼容性设计负载的主题。