一、JSON 基础概念
1. JSON 与 JavaScript 对象的区别
javascript
复制代码
// JavaScript 对象
const jsObj = {
name: "John",
age: 30,
isActive: true,
sayHello: function() { return "Hello"; }, // 函数
date: new Date(), // Date 对象
undefined: undefined, // undefined
[Symbol("id")]: 123 // Symbol
};
// JSON 字符串(合法的 JSON 格式)
const jsonStr = `{
"name": "John",
"age": 30,
"isActive": true,
"hobbies": ["reading", "coding"],
"address": {
"city": "New York",
"zip": "10001"
}
}`;
2. JSON 数据格式特点
- 仅支持的数据类型 :
- 字符串(必须双引号)
- 数字
- 布尔值
- 数组
- 对象
null
- 不支持的类型 :
- 函数
Date 对象
undefined
Symbol
- 循环引用
二、核心 API 方法
1. JSON.parse() - 字符串转对象
javascript
复制代码
// 基本使用
const jsonStr = '{"name":"John","age":30}';
const obj = JSON.parse(jsonStr);
console.log(obj.name); // "John"
// 第二个参数:reviver 函数(转换函数)
const jsonStr2 = '{"name":"John","age":30,"dob":"1990-01-01"}';
const obj2 = JSON.parse(jsonStr2, (key, value) => {
if (key === 'dob') {
return new Date(value); // 将字符串转为 Date 对象
}
if (key === 'age' && value < 0) {
return 0; // 验证并修正数据
}
return value;
});
// 解析包含特殊字符
const jsonStr3 = '{"message":"Hello\\nWorld"}';
JSON.parse(jsonStr3); // message: "Hello\nWorld"
2. JSON.stringify() - 对象转字符串
javascript
复制代码
const obj = {
name: "John",
age: 30,
isActive: true,
hobbies: ["reading", "coding"],
address: {
city: "New York"
}
};
// 基本使用
const jsonStr = JSON.stringify(obj);
// {"name":"John","age":30,"isActive":true,"hobbies":["reading","coding"],"address":{"city":"New York"}}
// 第二个参数:replacer(过滤器)
const jsonStr2 = JSON.stringify(obj, ['name', 'age']); // 只保留指定属性
// {"name":"John","age":30}
// 第二个参数:replacer 函数
const jsonStr3 = JSON.stringify(obj, (key, value) => {
if (typeof value === 'string') {
return value.toUpperCase(); // 字符串转大写
}
if (key === 'age') {
return undefined; // 排除 age 属性
}
return value;
});
// 第三个参数:格式化(空格数或字符串)
const jsonStr4 = JSON.stringify(obj, null, 2); // 2 空格缩进
const jsonStr5 = JSON.stringify(obj, null, '\t'); // 制表符缩进
// 处理 toJSON 方法
const objWithToJSON = {
name: "John",
date: new Date(),
toJSON() {
return {
name: this.name,
timestamp: this.date.getTime()
};
}
};
JSON.stringify(objWithToJSON); // {"name":"John","timestamp":...}
三、数据处理技巧
1. 深度克隆对象
javascript
复制代码
// 使用 JSON 进行深拷贝(有限制)
const original = {
name: "John",
hobbies: ["reading", "coding"],
address: { city: "NYC" }
};
const cloned = JSON.parse(JSON.stringify(original));
// 注意:会丢失函数、Date对象等
const problematic = {
date: new Date(),
func: () => console.log("hi"),
undefined: undefined,
infinity: Infinity,
nan: NaN
};
JSON.parse(JSON.stringify(problematic));
// {"date":"2024-01-01T00:00:00.000Z","infinity":null,"nan":null}
2. 数据验证与清洗
javascript
复制代码
// 验证 JSON 字符串是否有效
function isValidJSON(str) {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
}
// 安全解析
function safeJSONParse(str, defaultValue = {}) {
try {
return JSON.parse(str);
} catch (error) {
console.warn('JSON 解析失败:', error.message);
return defaultValue;
}
}
// 数据清洗
function cleanJSONData(obj) {
return JSON.parse(JSON.stringify(obj, (key, value) => {
// 移除空字符串
if (value === "") return undefined;
// 移除 null
if (value === null) return undefined;
// 处理特殊数字
if (typeof value === 'number' && !isFinite(value)) {
return null;
}
return value;
}));
}
3. 自定义序列化
javascript
复制代码
// 处理特殊类型
class User {
constructor(name, birthDate) {
this.name = name;
this.birthDate = birthDate;
}
toJSON() {
return {
name: this.name,
birthDate: this.birthDate.toISOString().split('T')[0],
age: new Date().getFullYear() - this.birthDate.getFullYear()
};
}
static fromJSON(json) {
return new User(json.name, new Date(json.birthDate));
}
}
const user = new User("Alice", new Date(1990, 0, 1));
const json = JSON.stringify(user);
const restoredUser = User.fromJSON(JSON.parse(json));
四、性能优化
1. 大数据量处理
javascript
复制代码
// 流式处理大数据
async function processLargeJSON(jsonString) {
// 使用 JSON.parse 的 reviver 进行流式处理
return JSON.parse(jsonString, (key, value) => {
// 在解析时即时处理数据
if (key === 'timestamp' && typeof value === 'string') {
return new Date(value);
}
return value;
});
}
// 分块处理
function* chunkJSONParse(jsonString, chunkSize = 1024 * 1024) {
const obj = JSON.parse(jsonString);
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i += chunkSize) {
const chunk = {};
for (let j = i; j < Math.min(i + chunkSize, keys.length); j++) {
const key = keys[j];
chunk[key] = obj[key];
}
yield chunk;
}
}
2. 缓存与复用
javascript
复制代码
// JSON 解析缓存
const jsonCache = new Map();
function parseWithCache(jsonString) {
if (jsonCache.has(jsonString)) {
return jsonCache.get(jsonString);
}
const result = JSON.parse(jsonString);
jsonCache.set(jsonString, result);
return result;
}
// 序列化缓存
const stringifyCache = new WeakMap();
function stringifyWithCache(obj) {
if (stringifyCache.has(obj)) {
return stringifyCache.get(obj);
}
const result = JSON.stringify(obj);
stringifyCache.set(obj, result);
return result;
}
五、高级应用场景
1. 配置文件处理
javascript
复制代码
// 配置文件解析
class ConfigManager {
constructor(configPath) {
this.configPath = configPath;
this.config = this.loadConfig();
}
loadConfig() {
try {
const configStr = fs.readFileSync(this.configPath, 'utf-8');
return JSON.parse(configStr, (key, value) => {
// 处理环境变量替换
if (typeof value === 'string' && value.startsWith('${') && value.endsWith('}')) {
const envVar = value.slice(2, -1);
return process.env[envVar] || value;
}
return value;
});
} catch (error) {
throw new Error(`配置文件解析失败: ${error.message}`);
}
}
saveConfig(config) {
const configStr = JSON.stringify(config, null, 2);
fs.writeFileSync(this.configPath, configStr);
}
}
2. API 数据处理
javascript
复制代码
// 统一的 API 响应处理
class APIResponseHandler {
static async handleResponse(response) {
const text = await response.text();
try {
const data = JSON.parse(text, (key, value) => {
// 转换日期字符串
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}/.test(value)) {
const date = new Date(value);
return isNaN(date.getTime()) ? value : date;
}
// 转换数字字符串
if (typeof value === 'string' && /^-?\d+(\.\d+)?$/.test(value)) {
return Number(value);
}
return value;
});
return {
success: true,
data,
status: response.status
};
} catch (error) {
return {
success: false,
error: 'JSON 解析失败',
raw: text,
status: response.status
};
}
}
}
3. JSON Schema 验证
javascript
复制代码
// 使用 JSON Schema 验证数据
const userSchema = {
type: "object",
required: ["name", "email"],
properties: {
name: { type: "string", minLength: 2 },
email: { type: "string", format: "email" },
age: { type: "integer", minimum: 0, maximum: 150 },
hobbies: {
type: "array",
items: { type: "string" }
}
}
};
function validateJSONAgainstSchema(jsonStr, schema) {
const obj = JSON.parse(jsonStr);
// 验证必需字段
if (schema.required) {
for (const field of schema.required) {
if (!(field in obj)) {
throw new Error(`缺少必需字段: ${field}`);
}
}
}
// 验证类型
for (const [key, value] of Object.entries(obj)) {
if (schema.properties && schema.properties[key]) {
const propSchema = schema.properties[key];
validateType(value, propSchema, key);
}
}
return obj;
}
六、安全注意事项
1. 防止 JSON 注入攻击
javascript
复制代码
// 不安全的方式
const userInput = '{"name": "John", "__proto__": {"admin": true}}';
const obj = JSON.parse(userInput); // 可能污染原型链
// 安全的解析方式
function safeJSONParse(str) {
const obj = JSON.parse(str);
// 检查并过滤危险属性
const dangerousKeys = ['__proto__', 'constructor', 'prototype'];
dangerousKeys.forEach(key => {
if (key in obj) {
delete obj[key];
}
});
return obj;
}
// 使用 Object.create(null)
const safeObj = Object.create(null);
Object.assign(safeObj, JSON.parse(userInput));
2. 处理循环引用
javascript
复制代码
// 检测循环引用
function stringifyWithCircularCheck(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular Reference]';
}
seen.add(value);
}
return value;
});
}
// 自定义循环引用处理
class CircularSafeJSON {
static stringify(obj) {
const cache = [];
const result = JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (cache.includes(value)) {
return `[Circular:${cache.indexOf(value)}]`;
}
cache.push(value);
}
return value;
});
return result;
}
}
七、实用工具函数
1. 常用工具函数集
javascript
复制代码
class JSONUtils {
// 美化输出
static prettyPrint(obj, indent = 2) {
return JSON.stringify(obj, null, indent);
}
// 压缩 JSON
static compress(obj) {
return JSON.stringify(obj);
}
// 深度比较两个 JSON 对象
static deepEqual(obj1, obj2) {
return JSON.stringify(obj1) === JSON.stringify(obj2);
}
// 获取 JSON 差异
static diff(obj1, obj2) {
const keys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
const differences = {};
for (const key of keys) {
const val1 = obj1[key];
const val2 = obj2[key];
if (JSON.stringify(val1) !== JSON.stringify(val2)) {
differences[key] = { from: val1, to: val2 };
}
}
return differences;
}
// 合并 JSON 对象
static merge(target, ...sources) {
sources.forEach(source => {
const parsed = typeof source === 'string' ?
JSON.parse(source) : source;
Object.assign(target, parsed);
});
return target;
}
}
2. JSON Path 查询
javascript
复制代码
// 简单的 JSON Path 查询
class JSONPath {
static query(obj, path) {
const parts = path.split('.');
let current = obj;
for (const part of parts) {
if (current === null || current === undefined) {
return undefined;
}
// 处理数组索引
if (part.includes('[') && part.includes(']')) {
const [key, indexStr] = part.split('[');
const index = parseInt(indexStr.replace(']', ''), 10);
current = current[key][index];
} else {
current = current[part];
}
}
return current;
}
static set(obj, path, value) {
const parts = path.split('.');
let current = obj;
for (let i = 0; i < parts.length - 1; i++) {
const part = parts[i];
if (!current[part] || typeof current[part] !== 'object') {
current[part] = {};
}
current = current[part];
}
current[parts[parts.length - 1]] = value;
return obj;
}
}
八、现代 JavaScript 特性
1. 使用 Proxy 监控 JSON 操作
javascript
复制代码
function createObservableJSON(obj) {
const history = [];
return new Proxy(obj, {
set(target, property, value) {
const oldValue = target[property];
target[property] = value;
history.push({
timestamp: new Date(),
property,
oldValue: JSON.stringify(oldValue),
newValue: JSON.stringify(value)
});
return true;
},
getHistory() {
return history;
}
});
}
2. 使用 Decorators(需要 Babel)
javascript
复制代码
// JSON 序列化装饰器
function jsonSerializable(target) {
target.prototype.toJSON = function() {
return Object.getOwnPropertyNames(this).reduce((obj, key) => {
const value = this[key];
// 排除函数
if (typeof value !== 'function') {
obj[key] = value;
}
return obj;
}, {});
};
return target;
}
@jsonSerializable
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
greet() {
return `Hello, ${this.name}`;
}
}
最佳实践总结
- 始终验证 JSON 字符串 :使用 try-catch 包装
JSON.parse()
- 合理使用 reviver/replacer:处理特殊数据类型
- 注意性能:大数据量时考虑分块处理
- 确保安全性:过滤危险属性,防止原型污染
- 保持向后兼容:处理新增/删除字段的情况
- 使用 Schema 验证:确保数据结构符合预期
- 考虑使用第三方库 :复杂需求可使用
ajv、json5 等库