JSON 教程

JSON(JavaScript Object Notation,JavaScript 对象表示法)是一种轻量级的数据交换格式。它易于阅读和编写,也易于机器解析和生成,是当今 Web 应用最主流的数据传输格式。

为什么 JSON 如此流行

  • 轻量:语法简洁,体积小,传输效率高
  • 语言无关:几乎所有编程语言都有 JSON 解析库
  • 易读:结构清晰,人和机器都能快速理解
  • 标准统一:前后端数据交换的"通用语言"

一个 JSON 示例

{
  "name": "CodeNest 教程",
  "version": "1.0",
  "authors": ["张三", "李四"],
  "topics": [
    { "id": 1, "title": "JSON 入门", "completed": true },
    { "id": 2, "title": "JSON 进阶", "completed": false }
  ],
  "rating": 4.8
}

JSON 的应用场景

场景说明
前后端数据交互AJAX/Fetch 请求返回的数据格式
API 接口RESTful API 的标准响应格式
配置文件如 package.json、tsconfig.json
数据存储NoSQL 数据库(MongoDB 等)的文档格式
数据交换不同系统间的数据传输

JSON 与 JavaScript 对象的关系

JSON 源自 JavaScript 的对象字面量,但两者并不完全相同:JSON 的键必须用双引号,且不能有函数、注释等。它们之间通过 JSON.parse() 和 JSON.stringify() 互相转换。

💡 提示

JSON 文件的扩展名是 .json,MIME 类型是 application/json。

JSON 简介

JSON(JavaScript Object Notation)是一种基于文本的、与语言无关的数据交换格式。它由 Douglas Crockford 在 2001 年推广,如今已成为 Web 世界的事实标准。

为什么选择 JSON

优点说明
轻量高效相比 XML,体积更小、解析更快
易于阅读结构直观,接近自然语言
跨语言Java/Python/JS/Go 等全部支持
与 JS 天然兼容可直接转为 JavaScript 对象

JSON 的诞生历史

在 JSON 出现之前,Web 应用主要用 XML 交换数据。XML 功能强大但过于繁琐,Crockford 从 JavaScript 的对象字面量中获得灵感,抽取出一套极简的文本格式,并迅速被各语言采纳。

JSON 基本结构

JSON 只有两种顶层结构:

  • 对象:{ "键": "值" },无序的键值对集合
  • 数组:[ "值1", "值2" ],有序的值列表

这两种结构可以任意嵌套组合,表达复杂的数据关系。

合法 JSON 的规范

  1. 键必须用双引号包裹(单引号不合法)
  2. 字符串值必须用双引号
  3. 不能有注释、尾随逗号
  4. 必须是 UTF-8 编码的纯文本
// ❌ 非法:单引号、尾随逗号、注释
{ 'name': '张三', }  // 错误

// ✅ 合法
{ "name": "张三" }
⚠️ 注意

在 JavaScript 中直接写 {name: "张三"} 是合法的对象字面量,但它不是合法 JSON —— JSON 要求键必须带双引号。

JSON 语法

JSON 语法是 JSON 数据交换的基础规则。掌握它,你就能读懂和编写任何 JSON 数据。

六种数据类型

类型示例说明
字符串"hello"双引号包裹,支持转义
数字42 / 3.14整数或浮点数,无引号
布尔值true / false小写,无引号
nullnull表示空值,小写
数组[1, 2, 3]有序集合,逗号分隔
对象{"k": "v"}键值对集合

对象语法

{
  "name": "CodeNest",
  "year": 2024,
  "isOpen": true,
  "founder": null
}

对象由 {} 包裹,键值对之间用逗号分隔,最后一对后不能有逗号。

数组语法

["HTML", "CSS", "JavaScript", 42, true, null]

数组由 [] 包裹,元素可以是任意类型,包括对象和其他数组。

嵌套结构

{
  "users": [
    { "id": 1, "name": "张三", "skills": ["Python", "AI"] },
    { "id": 2, "name": "李四", "skills": ["Java", "Go"] }
  ]
}

字符串转义

字符串中的特殊字符需要转义:\" 表示双引号,\\ 表示反斜杠,\n 表示换行,\u4e2d 表示 Unicode 字符(如"中")。

💡 提示

在线工具(如 JSON.cn、jsonlint.com)可以帮你校验 JSON 是否合法,并格式化显示。

JSON vs XML

JSON 和 XML 都用于结构化数据交换,但它们的设计哲学和适用场景有明显差异。

同一份数据的两种表达

<!-- XML 写法 -->
<user>
  <name>张三</name>
  <age>25</age>
  <skills>
    <skill>Python</skill>
    <skill>AI</skill>
  </skills>
</user>
// JSON 写法
{
  "name": "张三",
  "age": 25,
  "skills": ["Python", "AI"]
}

核心差异对比

对比项JSONXML
体积紧凑、更小标签冗余、更大
解析速度快较慢
可读性直观易读标签化、较繁琐
数据类型原生支持数字/布尔/数组全是文本
元数据/属性不支持支持属性、命名空间
注释不支持支持 <!-- -->
验证机制JSON SchemaDTD / XSD
扩展性较有限强大(自定义标签)

什么时候选 JSON

  • Web API 数据交换(绝大多数场景)
  • 移动端与前端数据
  • 追求简单高效的项目

什么时候选 XML

  • 文档型数据(如 SVG、RSS、XHTML)
  • SOAP Web 服务
  • 需要 XPath/XSLT 处理复杂文档
  • 金融、医疗等行业标准协议
💡 提示

对于绝大多数 Web 项目,JSON 是更明智的选择;XML 更适合"文档处理"而非"数据传输"。

JSON 对象

JSON 对象是一组无序的键值对集合,用花括号 {} 包裹,是 JSON 中最常用的结构。

对象的基本语法

{
  "name": "CodeNest",
  "url": "https://www.example.com",
  "visits": 1024
}

访问对象属性(JavaScript)

const user = JSON.parse('{"name": "张三", "age": 25}');

// 方式一:点语法
console.log(user.name);   // 张三

// 方式二:方括号语法(键名含特殊字符时)
console.log(user["age"]); // 25

// 修改与新增
user.city = "北京";
console.log(user);        // {name: "张三", age: 25, city: "北京"}

嵌套对象

{
  "user": {
    "profile": {
      "name": "李四",
      "address": { "city": "上海", "zip": "200000" }
    }
  }
}
// 逐层访问嵌套属性
console.log(data.user.profile.address.city);  // 上海

JavaScript 访问对象

const obj = { name: "王五", hobbies: ["读书", "跑步"] };

// 遍历键
for (const key in obj) {
  console.log(key + ": " + obj[key]);
}

// 获取所有键 / 值
console.log(Object.keys(obj));    // ["name", "hobbies"]
console.log(Object.values(obj));  // ["王五", ["读书", "跑步"]]

Python 访问 JSON 对象

import json

data = json.loads('{"name": "赵六", "age": 30}')
print(data["name"])     # 赵六
print(data.get("age"))  # 30(键不存在返回 None)
⚠️ 注意

JSON 对象的键顺序无关紧要,但同一个对象内不能有重复键(后者会覆盖前者)。

JSON 数组

JSON 数组是有序的值列表,用方括号 [] 包裹,元素之间用逗号分隔。数组中的元素可以是任意 JSON 类型。

数组的基本语法

[ "HTML", "CSS", "JavaScript" ]

混合类型数组

[ 1, "two", true, null, { "k": "v" }, [1, 2] ]

对象数组(最常用)

{
  "users": [
    { "id": 1, "name": "张三", "age": 25 },
    { "id": 2, "name": "李四", "age": 30 },
    { "id": 3, "name": "王五", "age": 28 }
  ]
}

JavaScript 遍历数组

const users = data.users;

// forEach 遍历
users.forEach(user => console.log(user.name));

// map 转换
const names = users.map(user => user.name);
console.log(names);  // ["张三", "李四", "王五"]

// filter 过滤
const young = users.filter(user => user.age < 28);
console.log(young);  // [张三]

// find 查找
const user = users.find(u => u.id === 2);
console.log(user.name);  // 李四

Python 处理 JSON 数组

import json

data = json.loads('[{"id": 1, "name": "张三"}, {"id": 2, "name": "李四"}]')

# 列表推导式
names = [u["name"] for u in data]
print(names)  # ['张三', '李四']

# 过滤
adults = [u for u in data if u["id"] > 1]
print(adults)

数组常用操作

操作JavaScript
长度arr.length
追加arr.push(4)
移除末尾arr.pop()
合并arr1.concat(arr2)
排序arr.sort()
💡 提示

API 返回列表数据时,顶层通常就是数组(如 [{...}, {...}]),或放在 {"data": [...]} 中。

JSON.parse()

JSON.parse() 是 JavaScript 内置方法,用于把 JSON 字符串解析为 JavaScript 对象或数组。

基本用法

const jsonStr = '{"name": "张三", "age": 25}';
const obj = JSON.parse(jsonStr);

console.log(obj.name);  // 张三
console.log(typeof obj); // object

解析数组

const list = JSON.parse('[1, 2, 3, 4]');
console.log(list.length);      // 4
console.log(list[0]);          // 1

典型应用:处理 API 响应

fetch("/api/users")
  .then(res => res.json())      // 内部就是 JSON.parse
  .then(data => {
    console.log(data);
  });

// 或用 XHR 手动解析
// const data = JSON.parse(xhr.responseText);

错误处理

如果字符串不是合法 JSON,JSON.parse() 会抛出 SyntaxError,必须用 try/catch 捕获:

const badJson = '{"name": "张三",}';  // 尾随逗号,非法

try {
  const obj = JSON.parse(badJson);
} catch (err) {
  console.error("JSON 解析失败:", err.message);
  // 处理:提示用户、使用默认值或重试
}

第二个参数:reviver 函数

可选参数 reviver 可在解析过程中转换每个键值对:

const json = '{"name": "张三", "birth": "1999-01-01"}';
const obj = JSON.parse(json, (key, value) => {
  if (key === "birth") {
    return new Date(value);   // 把日期字符串转成 Date 对象
  }
  return value;
});
console.log(obj.birth instanceof Date);  // true
⚠️ 注意

JSON.parse() 不能解析带单引号或注释的"伪 JSON",如果数据来自不可信来源,务必校验合法性。

JSON.stringify()

JSON.stringify() 与 JSON.parse() 相反,它把 JavaScript 对象/数组转换为 JSON 字符串,用于数据传输或存储。

基本用法

const user = { name: "张三", age: 25, isAdmin: true };
const jsonStr = JSON.stringify(user);

console.log(jsonStr);
// {"name":"张三","age":25,"isAdmin":true}

典型应用:发送数据到服务器

const user = { name: "李四", age: 30 };

fetch("/api/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(user)   // 序列化为 JSON 字符串
});

处理嵌套结构

const data = {
  title: "CodeNest",
  tags: ["教程", "编程"],
  meta: { version: "1.0", author: "老王" }
};

console.log(JSON.stringify(data));
// {"title":"CodeNest","tags":["教程","编程"],"meta":{"version":"1.0","author":"老王"}}

第二个参数:replacer 过滤器

可以传入数组(只保留指定键)或函数(自定义处理):

const user = { id: 1, name: "张三", password: "secret123" };

// 只保留指定键(常用于隐藏敏感字段)
console.log(JSON.stringify(user, ["id", "name"]));
// {"id":1,"name":"张三"}

// 函数过滤
console.log(JSON.stringify(user, (key, value) => {
  return key === "password" ? undefined : value;
}));
// {"id":1,"name":"张三"}

第三个参数:缩进美化

const data = { a: 1, b: { c: 2 } };

console.log(JSON.stringify(data, null, 2));
// {
//   "a": 1,
//   "b": {
//     "c": 2
//   }
// }

// 存储到 localStorage
localStorage.setItem("data", JSON.stringify(data));

注意事项

情况结果
值为 undefined / 函数 / Symbol对象中省略,数组中转为 null
值为 NaN / Infinity转为 null
循环引用抛出 TypeError
Date 对象转为 ISO 字符串
💡 提示

前后端约定的 JSON 字段命名常使用 camelCase(JS 侧)或 snake_case(Python 侧),可各自转换保持一致。

JSON 使用

综合使用 JSON.parse() 和 JSON.stringify(),就能完成前端与服务器之间完整的数据交换闭环。

完整流程示例

// 1. 把表单数据序列化后发送
const formData = {
  username: document.getElementById("username").value,
  email: document.getElementById("email").value
};

fetch("/api/register", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(formData)
})
.then(res => res.json())            // 2. 解析响应 JSON
.then(data => {
  // 3. 使用返回的数据
  if (data.success) {
    showMessage("注册成功,欢迎 " + data.user.name);
  } else {
    showError(data.message);
  }
})
.catch(err => console.error("网络错误:", err));

结合 AJAX 的综合案例

function searchBooks(keyword) {
  // 构造请求参数
  const params = { q: keyword, page: 1, limit: 10 };

  fetch("/api/books?" + new URLSearchParams(params))
    .then(res => res.json())
    .then(data => {
      // data: { total: 25, books: [{...}] }
      renderBooks(data.books);
      updatePagination(data.total);
    })
    .catch(err => console.error(err));
}

存储到本地

// 保存用户偏好(自动登录)
const prefs = { theme: "dark", lang: "zh-CN", lastVisit: Date.now() };
localStorage.setItem("prefs", JSON.stringify(prefs));

// 读取
const saved = JSON.parse(localStorage.getItem("prefs") || "{}");
console.log(saved.theme);  // dark

服务端处理 JSON(Python 示例)

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/api/register", methods=["POST"])
def register():
    # 接收 JSON 请求体
    data = request.get_json()
    username = data.get("username")
    email = data.get("email")

    # 业务逻辑(存库等)...

    # 返回 JSON 响应
    return jsonify({"success": True, "user": {"name": username}})

JSON 使用要点

环节要点
发送用 JSON.stringify(),Content-Type 设为 application/json
接收用 res.json() / JSON.parse()
校验解析前检查 res.ok,解析包 try/catch
安全服务端永远校验 JSON 结构,不直接信任
💡 提示

API 设计建议统一响应格式,例如 {"code": 0, "message": "ok", "data": {...}},方便前端统一处理。

JSONP 教程

JSONP(JSON with Padding)是一种历史悠久的跨域请求解决方案。在 CORS 出现之前,它是绕过浏览器同源策略的主流手段。

为什么需要 JSONP

浏览器同源策略限制了 AJAX 只能请求同源资源。但 <script> 标签不受此限制,JSONP 正是利用这一点:通过动态创建 <script> 标签加载跨域脚本。

JSONP 工作原理

  1. 前端定义一个回调函数(如 handleData)
  2. 动态创建 <script>,src 指向跨域接口,附带回调名参数
  3. 服务器把数据包装成 handleData({...}) 返回
  4. 浏览器执行这段"脚本",即调用回调函数

前端实现

function jsonp(url, callbackName, callback) {
  // 1. 挂载回调到全局
  window[callbackName] = function(data) {
    callback(data);
    // 清理
    script.parentNode.removeChild(script);
    delete window[callbackName];
  };

  // 2. 动态创建 script 标签
  const script = document.createElement("script");
  script.src = url + "?callback=" + callbackName;
  document.body.appendChild(script);
}

// 使用
jsonp("https://api.example.com/user", "handleUser", function(data) {
  console.log("跨域获取的数据:", data);
});

服务端响应格式

服务器不返回纯 JSON,而是返回"包裹着 JSON 的 JavaScript 调用":

// 请求:https://api.example.com/user?callback=handleUser
// 响应:
handleUser({"name": "张三", "city": "北京"});

JSONP 的优缺点

优点缺点
实现简单,兼容性极好只支持 GET 请求
老浏览器也能用无法处理错误(404 等静默失败)
无需服务器特殊配置有安全风险(XSS 注入面)
—需要服务端配合返回 JS 包装

现代替代方案:CORS

如今跨域首选 CORS(跨域资源共享):服务器在响应头加上 Access-Control-Allow-Origin,浏览器即允许跨域读取,且支持任意请求方法、可错误处理、更安全。

Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
⚠️ 注意

使用 JSONP 时绝不能信任回调返回的数据(可能被篡改),且回调名要避免与现有全局函数冲突。

第 1 / 10 节