📅 2026-03-24⏱ 阅读约 14 分钟👤 适合有经验的开发者
本文目录
掌握了 JSON 基础语法后,进阶技能能让你在复杂的真实项目中游刃有余。本文讲解六个高价值的 JSON 进阶主题,每个都配有完整的代码示例和实战建议。
1. JSON Schema:给你的 JSON 加上类型约束
JSON 本身没有类型约束——任何合法的 JSON 结构都可以通过解析。但在实际项目中,我们需要验证 API 接收的数据是否符合预期格式。JSON Schema 就是为此而生的。
JSON Schema 本身也是 JSON 格式,通过描述数据的结构、类型和约束来验证 JSON 数据。
基础 Schema 示例
// 定义"用户注册"数据的 Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "用户注册",
"type": "object",
"required": ["username", "email", "age"],
"properties": {
"username": {
"type": "string",
"minLength": 3,
"maxLength": 20,
"pattern": "^[a-zA-Z0-9_]+$",
"description": "用户名,只允许字母数字和下划线"
},
"email": {
"type": "string",
"format": "email",
"description": "有效的电子邮箱地址"
},
"age": {
"type": "integer",
"minimum": 18,
"maximum": 120,
"description": "年龄,必须是18岁以上"
},
"phone": {
"type": "string",
"pattern": "^1[3-9]\d{9}$",
"description": "可选的手机号"
},
"role": {
"type": "string",
"enum": ["admin", "user", "moderator"],
"default": "user"
}
},
"additionalProperties": false
}
常用验证关键字速查
| 关键字 | 适用类型 | 说明 |
|---|---|---|
type | 所有 | 指定类型:string/number/integer/boolean/array/object/null |
required | object | 必填字段列表 |
properties | object | 定义各字段的 Schema |
minLength/maxLength | string | 字符串长度范围 |
pattern | string | 正则表达式约束 |
minimum/maximum | number | 数值范围 |
enum | 所有 | 枚举值,只允许列表中的值 |
minItems/maxItems | array | 数组长度范围 |
items | array | 定义数组元素的 Schema |
format | string | 格式验证:email/date/time/uri 等 |
在 JavaScript 中使用 JSON Schema(ajv 库)
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv();
addFormats(ajv);
const schema = { /* 上面定义的 Schema */ };
const validate = ajv.compile(schema);
const data = { username: "zhangsan", email: "zs@example.com", age: 25 };
const valid = validate(data);
if (!valid) {
console.log("验证失败:", validate.errors);
// [{
// instancePath: '/age',
// message: 'must be >= 18'
// }]
} else {
console.log("数据格式有效 ✓");
}
2. JSONPath:像 XPath 一样查询 JSON
JSONPath 是一种用于查询 JSON 数据的路径表达式语言,类似于 XML 的 XPath。它让你可以从复杂的嵌套 JSON 中精准提取所需数据。
基础语法
| 表达式 | 含义 |
|---|---|
$ | 根节点 |
.key | 子节点(点记法) |
['key'] | 子节点(方括号记法) |
[n] | 数组第 n 个元素(从0开始) |
[*] | 数组所有元素 |
.. | 递归下降(所有层级) |
[?(@.price > 100)] | 过滤器:条件筛选 |
[0,2,4] | 选取多个索引 |
[1:4] | 切片(索引1到3) |
实战示例
// 示例数据
const data = {
"store": {
"books": [
{"title": "JavaScript高级程序设计", "price": 89, "category": "编程"},
{"title": "深入理解Java虚拟机", "price": 79, "category": "编程"},
{"title": "三体", "price": 59, "category": "科幻"},
{"title": "活着", "price": 39, "category": "文学"}
],
"name": "技术书店"
}
};
// JSONPath 查询示例:
// $.store.name → "技术书店"
// $.store.books[0].title → "JavaScript高级程序设计"
// $.store.books[*].title → 所有书名的数组
// $.store.books[?(@.price < 60)] → 价格低于60的书
// $..price → 所有 price 字段的值
// $.store.books[-1] → 最后一本书
3. 大型 JSON 性能优化
问题:解析大型 JSON 的性能陷阱
当 JSON 文件达到几十 MB 甚至几百 MB 时,一次性 JSON.parse() 会:占用大量内存(通常是文件大小的 5-10 倍)、导致主线程阻塞(浏览器卡顿)、可能造成 OOM(内存溢出)崩溃。
优化策略1:只提取需要的字段
// 如果只需要部分字段,用 replacer 过滤
const bigObj = { id: 1, name: "张三", largeData: "..." };
const minimal = JSON.stringify(bigObj, ["id", "name"]); // 只保留 id 和 name
优化策略2:使用 Web Worker 解析(浏览器端)
// 主线程
const worker = new Worker('json-worker.js');
worker.postMessage(largejsonString);
worker.onmessage = (e) => {
console.log("解析完成:", e.data);
};
// json-worker.js(在 Worker 中解析,不阻塞主线程)
self.onmessage = (e) => {
const result = JSON.parse(e.data);
self.postMessage(result);
};
优化策略3:分页和懒加载
对于大型列表数据,不要一次返回所有数据。使用分页参数:
// API 返回分页 JSON 而非全量数据
{
"data": [...], // 当前页数据
"pagination": {
"page": 1,
"pageSize": 20,
"total": 10000,
"hasNext": true
}
}
优化策略4:选择更高效的序列化格式
如果 JSON 性能成为瓶颈,可以考虑:
- MessagePack: JSON 的二进制版本,体积约为 JSON 的 50-70%,解析更快
- Protocol Buffers(Protobuf): Google 开发,需要定义 Schema,体积最小,速度最快
- CBOR: Concise Binary Object Representation,RFC 7049 标准
4. JSON 合并与深度克隆
浅合并 vs 深合并
const defaults = {
timeout: 30,
retry: 3,
db: { host: "localhost", port: 5432 }
};
const override = {
timeout: 60,
db: { host: "prod-db.example.com" }
};
// 浅合并(Object.assign 或展开运算符)
// 问题:db 字段会被完全替换,丢失 port
const shallow = { ...defaults, ...override };
// { timeout: 60, retry: 3, db: { host: "prod-db.example.com" } } ← port 丢失!
// 深合并(递归合并)
function deepMerge(target, source) {
const result = { ...target };
for (const key of Object.keys(source)) {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
result[key] = deepMerge(target[key] || {}, source[key]);
} else {
result[key] = source[key];
}
}
return result;
}
const merged = deepMerge(defaults, override);
// { timeout: 60, retry: 3, db: { host: "prod-db.example.com", port: 5432 } } ← 正确
深度克隆(最简方式)
// 用 JSON 做深拷贝(限制:不能处理函数、undefined、Date对象等)
const original = { name: "张三", scores: [95, 87, 92] };
const clone = JSON.parse(JSON.stringify(original));
// 现代方式(Node.js 17+,浏览器2022+)
const clone2 = structuredClone(original); // 推荐,支持更多类型
6. JSON 安全注意事项
防止 JSON 注入
永远不要用字符串拼接构造 JSON,始终使用 JSON.stringify():
// ❌ 危险:字符串拼接
const userInput = '"; "malicious": "payload';
const json = '{"name": "' + userInput + '"}';
// 产生: {"name": ""; "malicious": "payload"} ← 注入成功!
// ✅ 安全:使用 JSON.stringify()
const safeJson = JSON.stringify({ name: userInput });
// '{"name": "\"; \"malicious\": \"payload"}' ← 自动转义
防止原型污染
// ❌ 危险:直接将解析的 JSON 合并到对象
const data = JSON.parse('{"__proto__": {"isAdmin": true}}');
Object.assign({}, data); // 可能污染所有对象的原型!
// ✅ 安全:验证和清洗输入
// 使用 JSON Schema 验证,拒绝包含 __proto__、constructor 等敏感键的数据
💡 在开发和调试阶段,经常用 JSON格式化工具 检查数据结构,可以帮助你更快发现异常数据和潜在的安全问题。