JavaScript 中 JSON 的处理方法

一、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}`;
  }
}

最佳实践总结

  1. 始终验证 JSON 字符串 :使用 try-catch 包装 JSON.parse()
  2. 合理使用 reviver/replacer:处理特殊数据类型
  3. 注意性能:大数据量时考虑分块处理
  4. 确保安全性:过滤危险属性,防止原型污染
  5. 保持向后兼容:处理新增/删除字段的情况
  6. 使用 Schema 验证:确保数据结构符合预期
  7. 考虑使用第三方库 :复杂需求可使用 ajvjson5 等库
相关推荐
崔庆才丨静觅13 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606114 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了14 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅14 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅14 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅15 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment15 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅15 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊15 小时前
jwt介绍
前端
爱敲代码的小鱼15 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax