面向开发者的 JSON:语法、验证和常见错误
· 12分钟阅读
目录
什么是 JSON 以及为什么它很重要
JSON(JavaScript Object Notation)已成为网络数据交换的事实标准。尽管其名称暗示与 JavaScript 有关,但 JSON 是完全独立于语言的,几乎所有现代编程语言都支持它。
JSON 最初由 Douglas Crockford 在 2000 年代初期规范,作为 XML 的轻量级替代方案出现。其简洁性和人类可读性使其成为 API、配置文件和数据存储的完美格式。
如今,JSON 为从 REST API 和 NoSQL 数据库到配置文件和数据管道的一切提供支持。深入理解 JSON 不仅有帮助——它对现代软件开发至关重要。
快速提示:JSON 不是编程语言或数据库格式。它纯粹是一种数据序列化格式——一种将结构化数据表示为文本的方式。
JSON 语法规则
JSON 的语法看似简单,但其严格性让许多开发者措手不及。与 JavaScript 对象不同,JSON 对语法变化零容忍。
这是一个演示核心结构的有效 JSON 示例:
{
"name": "RunDev",
"version": 2,
"features": ["formatter", "validator", "converter"],
"config": {
"theme": "dark",
"autoSave": true
},
"deprecated": null
}
不可协商的规则
每个 JSON 文档都必须遵循这些严格规则:
- 键必须是双引号字符串 —
"name"有效,但name或'name'会导致解析错误 - 字符串必须使用双引号 —
"hello"可以,'hello'不行 - 不允许尾随逗号 —
[1, 2, 3]正确,[1, 2, 3,]会失败 - 不允许注释 —
// comment和/* comment */都无效 - 使用 null,而非 undefined — JavaScript 的
undefined在 JSON 中不存在 - 数字遵循特定格式 — 无前导零(
10而非010),无十六进制表示法,无Infinity或NaN
为什么如此严格?
JSON 的严格性是有意为之。通过消除歧义,JSON 确保任何有效的 JSON 文档都可以在所有平台和语言中以相同方式解析。这种可预测性使 JSON 在数据交换中可靠。
缺少注释经常受到批评,但它迫使开发者通过清晰的键名和结构使数据自文档化。对于需要注释的配置文件,考虑使用 JSONC(带注释的 JSON)或 JSON5。
专业提示:使用我们的 JSON 格式化工具自动修复常见语法错误,并确保您的 JSON 在部署前有效。
理解 JSON 数据类型
JSON 恰好支持六种数据类型。这个有限的集合保持了格式的简单性,同时涵盖了大多数数据表示需求。
| 类型 | 示例 | 注释 |
|---|---|---|
| 字符串 | "hello world" |
必须使用双引号。支持转义序列:\n、\t、\u0041、\"、\\ |
| 数字 | 42、3.14、-1、1e10 |
不区分整数和浮点数。无十六进制、八进制或特殊值如 Infinity |
| 布尔值 | true、false |
仅小写。"true" 是字符串,不是布尔值 |
| Null | null |
仅小写。表示有意缺少值 |
| 数组 | [1, "two", true] |
有序列表。可以包含混合类型(尽管同质数组更清晰) |
| 对象 | {"key": "value"} |
无序键值对。键必须是字符串且应唯一 |
字符串和转义序列
JSON 字符串支持几种特殊字符的转义序列:
\"— 双引号\\— 反斜杠\/— 正斜杠(可选,但有效)\b— 退格\f— 换页\n— 换行\r— 回车\t— 制表符\uXXXX— Unicode 字符(例如,\u0041表示 "A")
多行字符串必须使用转义序列——不允许字面换行:
{
"correct": "Line 1\nLine 2\nLine 3",
"incorrect": "Line 1
Line 2
Line 3"
}
数字:允许什么和不允许什么
JSON 数字比大多数编程语言更具限制性:
- 有效:
42、-17、3.14159、-0.5、1.23e10、1.23e-10 - 无效:
.5(必须是0.5)、010(八进制表示法)、0xFF(十六进制)、Infinity、NaN
JSON 规范没有定义精度限制,但大多数实现使用 IEEE 754 双精度浮点数,这意味着整数在 2^53 - 1(9,007,199,254,740,991)之前是安全的。
专业提示:对于超出 JavaScript 安全整数范围的大整数,考虑将它们存储为字符串以防止解析期间的精度损失。
对象和键唯一性
虽然 JSON 规范规定对象键应该是唯一的,但它没有规定解析器应如何处理重复项。不同的实现行为不同:
- JavaScript 的
JSON.parse()保留最后一次出现 - 一些验证器完全拒绝重复键
- 其他保留第一次出现
最佳实践:始终确保您的键是唯一的,以避免不同解析器之间的不可预测行为。
常见错误及如何避免
即使是经验丰富的开发者也会犯 JSON 语法错误。以下是最常见的错误及其修复方法:
| 错误 | 错误示例 | 正确示例 |
|---|---|---|
| 单引号 | {'name': 'test'} |
{"name": "test"} |
| 尾随逗号 | {"a": 1, "b": 2,} |
{"a": 1, "b": 2} |
| 未加引号的键 | {name: "test"} |
{"name": "test"} |
| 注释 | {"a": 1 // comment} |
删除注释或使用 JSONC |
| Undefined 值 | {"a": undefined} |
{"a": null} |
| 前导零 | {"port": 080} |
{"port": 80} |
| 字面换行 | "line1 |
"line1\nline2" |
| 函数值 | {"fn": function(){}} |
不支持——使用字符串或重构 |
JavaScript 对象陷阱
JSON 错误的最大来源是将其视为 JavaScript 对象字面量语法。它们看起来相似但有关键区别:
// 有效的 JavaScript 对象
const jsObj = {
name: 'test', // 未加引号的键,单引号
age: 30,
active: true,
getData: function() { return this.name; }, // 允许函数
// 允许注释
};
// 有效的 JSON(作为字符串)
const jsonStr = `{
"name": "test",
"age": 30,
"active": true
}`;
记住:JSON 是一种数据格式,不是代码。它不能包含函数、注释或可执行逻辑。
字符编码问题
JSON 必须以 UTF-8、UTF-16 或 UTF-32 编码。UTF-8 是最常见和推荐的编码。注意:
- 文件开头的 BOM(字节顺序标记)——一些解析器会拒绝它
- 可能导致解析失败的无效 UTF-8 序列
- 必须转义的控制字符(U+0000 到 U+001F)
快速提示:将您的 JSON 粘贴到我们的 JSON 验证器中,立即识别语法错误、编码问题和结构问题。
验证 JSON:工具和技术
验证对于在运行时失败之前捕获错误至关重要。以下是如何在不同环境中验证 JSON。
命令行验证
使用内置工具快速验证:
# Python(内置,随处可用)
python3 -m json.tool data.json
# 美化打印和验证
python3 -m json.tool data.json output.json
# jq(强大的 JSON 处理器)
jq . data.json
# jq 带错误详情
jq . data.json || echo "Invalid JSON"
# Node.js 单行命令
node -e "console.log(JSON.parse(require('fs').readFileSync('data.json')))"
# 使用 jsonlint(通过 npm 安装)
jsonlint data.json
程序化验证
在应用程序代码中验证 JSON:
// JavaScript/Node.js
function isValidJSON(str) {
try {
JSON.parse(str);
return true;
} catch (e) {
console.error('JSON Error:', e.message);
return false;
}
}
# Python
import json
def is_valid_json(json_str):
try:
json.loads(json_str)
return True
except json.JSONDecodeError as e:
print(f'JSON Error: {e}')
return False
// Go
import "encoding/json"
func isValidJSON(data []byte) bool {
var js json.RawMessage
return json.Unmarshal(data, &js) == nil
}
模式验证
语法验证仅检查 JSON 是否格式良好。模式验证确保数据结构和类型符合您的要求。
JSON Schema 是定义 JSON 结构的标准:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"age": {
"type": "integer",
"minimum": 0,
"maximum": 150
},
"email": {
"type": "string",
"format": "email"
}
},
"required": ["name", "email"]
}
流行的模式验证库:
- JavaScript:Ajv(最快)、joi、yup
- Python:jsonschema、pydantic
- Go:gojsonschema、jsonschema
- Java:everit-org/json-schema、networknt/json-schema-validator
专业提示:使用我们的 JSON Schema 验证器在代码中实现之前针对示例数据测试您的模式。
在线验证工具
用于开发期间的快速检查:
- RunDev JSON 验证器 — 带详细错误消息的即时验证
- RunDev JSON 格式化工具 — 一步验证和美化
- JSONLint — 经典在线验证器
- JSON Schema Validator — 交互式测试模式
解析和序列化最佳实践
解析将 JSON 文本转换为原生数据结构。序列化则相反。这两种操作都有性能和安全影响。
安全解析实践
始终在错误处理中包装 JSON 解析:
// JavaScript - 基本解析
try {
const data = JSON.parse(jsonString);
// 使用数据
} catch (error) {
console.error('Failed to parse JSON:', error.message);
// 适当处理错误
}
// JavaScript - 使用 reviver 函数
const data = JSON.parse(jsonString, (key, value) => {
// 从字符串转换日期
if (key.endsWith('Date') && typeof value === 'string') {
return new Date(value);
}
return value;
});
序列化最佳实践
控制对象如何转换为 JSON:
// JavaScript - 基本序列化
const json = JSON.stringify(data);
// 带缩进的美化打印
const json = JSON.stringify(data, null, 2);
// 使用 replacer 的自定义序列化
const json = JSON.stringify(data, (key, value) => {
// 删除敏感字段
if (key === 'password' || key === 'apiKey') {
return undefined;
}
// 将 BigInt 转换为字符串
if (typeof value === 'bigint'