Skip to content

JavaScript JSON

什么是 JSON

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它基于 JavaScript 的对象字面量语法,但独立于 JavaScript。JSON 是一种文本格式,易于人类阅读和编写,同时也易于机器解析和生成。

JSON 的特点

  1. 轻量级:JSON 格式简洁,数据量小,传输速度快
  2. 易读易写:JSON 格式符合人类阅读习惯,易于编写和理解
  3. 跨语言:JSON 可以被多种编程语言解析和生成
  4. 基于 JavaScript:JSON 语法基于 JavaScript 对象字面量语法,但有一些差异
  5. 文本格式:JSON 是纯文本格式,可以通过网络传输

JSON 语法

1. 基本语法规则

  • JSON 数据由键值对组成
  • 键必须是字符串,用双引号包围
  • 值可以是字符串、数字、布尔值、数组、对象或 null
  • 键值对之间用逗号分隔
  • 对象用花括号 {} 包围
  • 数组用方括号 [] 包围

2. JSON 与 JavaScript 对象的区别

特性JSONJavaScript 对象
必须用双引号包围可以不用引号,或用单引号、双引号
不能包含函数、undefined、NaN、Infinity可以包含函数、undefined、NaN、Infinity
字符串必须用双引号包围可以用单引号、双引号或反引号
末尾逗号不允许允许
注释不允许允许

3. JSON 示例

基本 JSON 对象

json
{
  "name": "John",
  "age": 30,
  "isStudent": false,
  "address": {
    "street": "123 Main St",
    "city": "New York",
    "country": "USA"
  },
  "hobbies": ["reading", "coding", "traveling"],
  "birthdate": "1993-01-01",
  "score": null
}

基本 JSON 数组

json
[
  {
    "name": "John",
    "age": 30
  },
  {
    "name": "Jane",
    "age": 25
  },
  {
    "name": "Bob",
    "age": 35
  }
]

JSON 方法

JavaScript 提供了两个内置方法来处理 JSON:

  • JSON.parse():将 JSON 字符串解析为 JavaScript 对象
  • JSON.stringify():将 JavaScript 对象序列化为 JSON 字符串

1. JSON.parse()

语法JSON.parse(text[, reviver])

  • text:要解析的 JSON 字符串
  • reviver:可选,转换结果的函数,用于修改解析过程

返回值:解析后的 JavaScript 对象

示例

javascript
// 基本使用
var jsonString =
  '{"name": "John", "age": 30, "hobbies": ["reading", "coding"]}';
var obj = JSON.parse(jsonString);

console.log(obj); // { name: "John", age: 30, hobbies: ["reading", "coding"] }
console.log(obj.name); // "John"
console.log(obj.age); // 30
console.log(obj.hobbies[0]); // "reading"

// 使用 reviver 函数
var jsonString = '{"name": "John", "age": 30, "birthdate": "1993-01-01"}';
var obj = JSON.parse(jsonString, function (key, value) {
  if (key === "birthdate") {
    return new Date(value);
  }
  return value;
});

console.log(obj.birthdate); // Date 对象
console.log(obj.birthdate.getFullYear()); // 1993

// 解析 JSON 数组
var jsonArrayString =
  '[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]';
var arr = JSON.parse(jsonArrayString);

console.log(arr); // [{ name: "John", age: 30 }, { name: "Jane", age: 25 }]
console.log(arr[0].name); // "John"

错误处理

javascript
// 无效的 JSON 字符串
var invalidJson = '{"name": "John", "age": }';

try {
  var obj = JSON.parse(invalidJson);
  console.log(obj);
} catch (error) {
  console.error('解析错误:', error.message);
  // 输出: 解析错误: Unexpected end of JSON input
}

// 包含注释的 JSON(无效)
var jsonWithComments = '{
  // 这是注释
  "name": "John",
  "age": 30
}';

try {
  var obj = JSON.parse(jsonWithComments);
  console.log(obj);
} catch (error) {
  console.error('解析错误:', error.message);
  // 输出: 解析错误: Unexpected token / in JSON at position 2
}

2. JSON.stringify()

语法JSON.stringify(value[, replacer[, space]])

  • value:要序列化的 JavaScript 对象
  • replacer:可选,转换结果的函数或数组,用于过滤和转换值
  • space:可选,控制输出的缩进格式

返回值:序列化后的 JSON 字符串

示例

javascript
// 基本使用
var obj = {
  name: "John",
  age: 30,
  hobbies: ["reading", "coding"],
  address: {
    street: "123 Main St",
    city: "New York",
  },
};

var jsonString = JSON.stringify(obj);
console.log(jsonString);
// 输出: {"name":"John","age":30,"hobbies":["reading","coding"],"address":{"street":"123 Main St","city":"New York"}}

// 使用 space 参数格式化输出
var prettyJson = JSON.stringify(obj, null, 2);
console.log(prettyJson);
// 输出格式化的 JSON:
// {
//   "name": "John",
//   "age": 30,
//   "hobbies": [
//     "reading",
//     "coding"
//   ],
//   "address": {
//     "street": "123 Main St",
//     "city": "New York"
//   }
// }

// 使用 replacer 函数
var obj = {
  name: "John",
  age: 30,
  password: "secret123", // 敏感信息,不希望序列化
  birthdate: new Date(1993, 0, 1),
};

var jsonString = JSON.stringify(
  obj,
  function (key, value) {
    if (key === "password") {
      return undefined; // 返回 undefined 会跳过该属性
    }
    if (key === "birthdate") {
      return value.toISOString(); // 转换日期为 ISO 字符串
    }
    return value;
  },
  2
);

console.log(jsonString);
// 输出: {
//   "name": "John",
//   "age": 30,
//   "birthdate": "1993-01-01T00:00:00.000Z"
// }

// 使用 replacer 数组
var obj = {
  name: "John",
  age: 30,
  hobbies: ["reading", "coding"],
  address: {
    street: "123 Main St",
    city: "New York",
  },
};

// 只序列化 name 和 hobbies 属性
var jsonString = JSON.stringify(obj, ["name", "hobbies"], 2);
console.log(jsonString);
// 输出: {
//   "name": "John",
//   "hobbies": [
//     "reading",
//     "coding"
//   ]
// }

// 序列化数组
var arr = [
  { name: "John", age: 30 },
  { name: "Jane", age: 25 },
];
var jsonString = JSON.stringify(arr, null, 2);
console.log(jsonString);
// 输出: [
//   {
//     "name": "John",
//     "age": 30
//   },
//   {
//     "name": "Jane",
//     "age": 25
//   }
// ]

注意事项

  • undefined、函数和 Symbol 会被忽略(在对象中)或转换为 null(在数组中)
  • Date 对象会被转换为 ISO 字符串
  • NaNInfinity-Infinity 会被转换为 null
  • 循环引用会抛出错误
javascript
// 包含 undefined、函数和 Symbol 的对象
var obj = {
  name: "John",
  age: undefined,
  greet: function () {
    console.log("Hello");
  },
  symbol: Symbol("test"),
};

var jsonString = JSON.stringify(obj);
console.log(jsonString);
// 输出: {"name":"John"}(undefined、函数和 Symbol 被忽略)

// 包含 Date 对象的对象
var obj = {
  name: "John",
  birthdate: new Date(1993, 0, 1),
};

var jsonString = JSON.stringify(obj);
console.log(jsonString);
// 输出: {"name":"John","birthdate":"1993-01-01T00:00:00.000Z"}

// 包含 NaN 和 Infinity 的对象
var obj = {
  name: "John",
  score: NaN,
  max: Infinity,
  min: -Infinity,
};

var jsonString = JSON.stringify(obj);
console.log(jsonString);
// 输出: {"name":"John","score":null,"max":null,"min":null}

// 循环引用(会抛出错误)
var obj1 = { name: "John" };
var obj2 = { name: "Jane" };
obj1.friend = obj2;
obj2.friend = obj1; // 循环引用

try {
  var jsonString = JSON.stringify(obj1);
  console.log(jsonString);
} catch (error) {
  console.error("序列化错误:", error.message);
  // 输出: 序列化错误: Converting circular structure to JSON
}

JSON 的应用场景

1. 网络数据传输

JSON 是 Web 应用中最常用的数据交换格式,用于客户端和服务器之间的数据传输。

示例:使用 Fetch API 获取 JSON 数据

javascript
// 从服务器获取 JSON 数据
fetch("https://api.example.com/users")
  .then((response) => response.json()) // 解析 JSON 响应
  .then((data) => {
    console.log(data); // 处理解析后的数据
  })
  .catch((error) => {
    console.error("Error:", error);
  });

// 向服务器发送 JSON 数据
fetch("https://api.example.com/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json", // 设置内容类型为 JSON
  },
  body: JSON.stringify({
    // 序列化对象为 JSON 字符串
    name: "John",
    age: 30,
    email: "john@example.com",
  }),
})
  .then((response) => response.json())
  .then((data) => {
    console.log("Success:", data);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

2. 本地存储

JSON 常用于本地存储,如 localStorage 和 sessionStorage,因为这些存储机制只能存储字符串。

示例:使用 localStorage 存储 JSON 数据

javascript
// 存储 JSON 数据到 localStorage
var user = {
  name: "John",
  age: 30,
  preferences: {
    theme: "dark",
    notifications: true,
  },
};

localStorage.setItem("user", JSON.stringify(user)); // 序列化并存储

// 从 localStorage 获取 JSON 数据
var storedUser = localStorage.getItem("user");
if (storedUser) {
  var userObj = JSON.parse(storedUser); // 解析 JSON 字符串
  console.log(userObj); // { name: "John", age: 30, ... }
}

// 更新存储的数据
user.age = 31;
localStorage.setItem("user", JSON.stringify(user));

// 清除存储的数据
localStorage.removeItem("user");

3. 配置文件

JSON 常用于配置文件,因为它结构清晰,易于阅读和修改。

示例:配置文件

json
// config.json
{
  "api": {
    "baseUrl": "https://api.example.com",
    "timeout": 5000,
    "headers": {
      "Content-Type": "application/json"
    }
  },
  "app": {
    "name": "My App",
    "version": "1.0.0",
    "debug": true
  }
}
javascript
// 加载配置文件
fetch("config.json")
  .then((response) => response.json())
  .then((config) => {
    console.log("API Base URL:", config.api.baseUrl);
    console.log("App Name:", config.app.name);
    // 使用配置
  });

4. 数据交换

JSON 常用于不同系统之间的数据交换,因为它是一种通用格式,几乎所有编程语言都支持。

示例:Node.js 中处理 JSON

javascript
// Node.js 中读取 JSON 文件
const fs = require("fs");

// 读取 JSON 文件
const config = JSON.parse(fs.readFileSync("config.json", "utf8"));
console.log(config);

// 写入 JSON 文件
const user = {
  name: "John",
  age: 30,
};

fs.writeFileSync("user.json", JSON.stringify(user, null, 2));
console.log("User data written to user.json");

5. 状态管理

在前端框架中,JSON 常用于状态管理,如 Redux 和 Vuex。

示例:Redux 中的状态序列化

javascript
// Redux 中保存状态到 localStorage
const saveState = (state) => {
  try {
    const serializedState = JSON.stringify(state);
    localStorage.setItem("reduxState", serializedState);
  } catch (err) {
    console.error("Error saving state:", err);
  }
};

// 从 localStorage 加载状态
const loadState = () => {
  try {
    const serializedState = localStorage.getItem("reduxState");
    if (serializedState === null) {
      return undefined;
    }
    return JSON.parse(serializedState);
  } catch (err) {
    console.error("Error loading state:", err);
    return undefined;
  }
};

// 使用
const store = createStore(
  rootReducer,
  loadState() // 加载保存的状态
);

// 订阅状态变化,保存到 localStorage
store.subscribe(() => {
  saveState(store.getState());
});

JSON 的最佳实践

1. 格式规范

  • 使用双引号包围键和字符串值
  • 保持缩进一致,提高可读性
  • 避免使用注释(JSON 不支持)
  • 确保 JSON 格式正确,特别是嵌套结构

2. 性能优化

  • 对于大型 JSON 数据,考虑使用流式解析
  • 避免不必要的嵌套层次,保持数据结构扁平
  • 只包含必要的数据,减少数据传输量
  • 使用压缩(如 gzip)减少 JSON 数据的大小

3. 安全性

  • 验证 JSON 数据的来源,避免注入攻击
  • 对 JSON 数据进行适当的转义,防止 XSS 攻击
  • 限制 JSON 数据的大小,防止 DoS 攻击
  • 对敏感信息进行加密,不要明文存储

4. 错误处理

  • 使用 try-catch 捕获 JSON 解析错误
  • 提供友好的错误信息,帮助用户理解问题
  • 对无效的 JSON 数据进行适当的处理,避免应用崩溃

5. 兼容性

  • 考虑浏览器兼容性,特别是对于较旧的浏览器
  • 对于不支持 JSON 方法的浏览器,可以使用 polyfill
  • 确保 JSON 数据的格式在不同语言和平台之间兼容

JSON 相关工具

1. JSON 验证工具

  • JSONLint:在线验证 JSON 格式
  • JSON Formatter:在线格式化和验证 JSON
  • Visual Studio Code:内置 JSON 验证和格式化功能

2. JSON 处理库

  • json5:扩展的 JSON 格式,支持注释和尾随逗号
  • circular-json:处理循环引用的 JSON 库
  • jsonschema:JSON Schema 验证库
  • fast-json-stringify:高性能的 JSON 序列化库

3. 浏览器开发者工具

现代浏览器的开发者工具提供了 JSON 查看和格式化功能:

  • Network 面板:查看和格式化网络请求中的 JSON 数据
  • Console 面板:使用 JSON.stringify(obj, null, 2) 格式化输出对象
  • Application 面板:查看 localStorage 和 sessionStorage 中的 JSON 数据

常见问题及解决方案

1. JSON 解析错误

问题JSON.parse() 抛出解析错误。

解决方案

  • 确保 JSON 格式正确,特别是键值对的语法
  • 检查是否有尾随逗号、注释或其他无效字符
  • 使用 JSON 验证工具验证 JSON 格式
  • 使用 try-catch 捕获错误并提供友好的错误信息

2. 循环引用导致序列化失败

问题JSON.stringify() 遇到循环引用时抛出错误。

解决方案

  • 避免对象之间的循环引用
  • 使用第三方库(如 circular-json)处理循环引用
  • 在序列化前手动打破循环引用

3. 大型 JSON 数据导致性能问题

问题:处理大型 JSON 数据时性能下降。

解决方案

  • 使用流式解析器处理大型 JSON 数据
  • 减少 JSON 数据的大小,只包含必要的信息
  • 使用压缩减少数据传输量
  • 考虑使用其他数据格式,如 Protocol Buffers 或 MessagePack

4. 日期序列化问题

问题Date 对象被序列化为字符串后,解析时不会自动转换回 Date 对象。

解决方案

  • 使用 JSON.parse()reviver 函数手动转换日期
  • 在序列化前将日期转换为 ISO 字符串,解析后手动转换回 Date 对象
  • 使用第三方库处理日期序列化和解析

5. 敏感信息泄露

问题:JSON 数据中包含敏感信息,如密码或 API 密钥。

解决方案

  • 不要在客户端存储敏感信息
  • 在序列化前过滤掉敏感信息
  • 对敏感信息进行加密
  • 使用 HTTPS 传输 JSON 数据

总结

JSON 是一种轻量级、易读易写的数据交换格式,它基于 JavaScript 的对象字面量语法,但独立于 JavaScript。JavaScript 提供了 JSON.parse()JSON.stringify() 方法来处理 JSON 数据。

JSON 的主要应用场景包括:

  • 网络数据传输
  • 本地存储
  • 配置文件
  • 数据交换
  • 状态管理

使用 JSON 时,应注意以下几点:

  • 确保 JSON 格式正确
  • 处理错误情况
  • 优化性能
  • 确保安全性
  • 考虑兼容性

通过掌握 JSON 的使用方法和最佳实践,你可以更有效地在 JavaScript 应用中处理数据,提高应用的性能和可靠性。