7.1.2 JSON 数据格式
一句话破题
JSON 是前后端交流的"普通话"——浏览器认识它,服务器认识它,几乎所有编程语言都能读写它。
JSON 基础
json
{
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"age": 25,
"isActive": true,
"tags": ["developer", "designer"],
"profile": {
"avatar": "avatar.png",
"bio": "Hello world"
},
"createdAt": "2024-01-15T10:30:00Z"
}支持的数据类型
| 类型 | 示例 | 注意事项 |
|---|---|---|
| 字符串 | "hello" | 必须用双引号 |
| 数字 | 123, 3.14 | 不支持 NaN、Infinity |
| 布尔 | true, false | 小写 |
| null | null | 表示空值 |
| 数组 | [1, 2, 3] | 有序列表 |
| 对象 | {"key": "value"} | 键值对 |
不支持的类型
typescript
// ❌ 这些类型 JSON 不支持
const data = {
date: new Date(), // Date 对象
fn: () => {}, // 函数
undef: undefined, // undefined
symbol: Symbol('x'), // Symbol
bigint: 123n, // BigInt
}
// 序列化后:
JSON.stringify(data)
// { "date": "2024-01-15T10:30:00.000Z" }
// 函数、undefined、Symbol 会被忽略
// BigInt 会报错序列化与反序列化
基本用法
typescript
// 序列化:对象 → 字符串
const user = { name: 'Alice', age: 25 }
const jsonString = JSON.stringify(user)
// '{"name":"Alice","age":25}'
// 反序列化:字符串 → 对象
const parsed = JSON.parse(jsonString)
// { name: 'Alice', age: 25 }处理日期
typescript
// 问题:Date 序列化后变成字符串
const data = { createdAt: new Date() }
const json = JSON.stringify(data)
// '{"createdAt":"2024-01-15T10:30:00.000Z"}'
const parsed = JSON.parse(json)
parsed.createdAt // 字符串,不是 Date!
// 解决方案 1:使用 reviver 函数
const parsed2 = JSON.parse(json, (key, value) => {
if (key === 'createdAt') {
return new Date(value)
}
return value
})
// 解决方案 2:使用 superjson(推荐)
import superjson from 'superjson'
const json2 = superjson.stringify({ date: new Date() })
const parsed3 = superjson.parse(json2)
// parsed3.date 是真正的 Date 对象API 响应格式规范
统一的响应结构
typescript
// 成功响应
interface SuccessResponse<T> {
success: true
data: T
}
// 列表响应(带分页)
interface ListResponse<T> {
success: true
data: T[]
pagination: {
page: number
pageSize: number
total: number
totalPages: number
}
}
// 错误响应
interface ErrorResponse {
success: false
error: {
code: string
message: string
}
}实际示例
typescript
// 获取单个用户
// GET /api/users/123
{
"success": true,
"data": {
"id": "123",
"name": "Alice",
"email": "alice@example.com"
}
}
// 获取用户列表
// GET /api/users?page=1&pageSize=10
{
"success": true,
"data": [
{ "id": "1", "name": "Alice" },
{ "id": "2", "name": "Bob" }
],
"pagination": {
"page": 1,
"pageSize": 10,
"total": 100,
"totalPages": 10
}
}
// 错误响应
// GET /api/users/999
{
"success": false,
"error": {
"code": "USER_NOT_FOUND",
"message": "用户不存在"
}
}命名规范
前端常用 camelCase
typescript
// 前端 TypeScript
interface User {
userId: string
firstName: string
lastName: string
createdAt: string
}后端可能用 snake_case
python
# Python/数据库
{
"user_id": "123",
"first_name": "Alice",
"last_name": "Smith",
"created_at": "2024-01-15T10:30:00Z"
}转换方案
typescript
// 使用库进行转换
import camelcaseKeys from 'camelcase-keys'
import snakecaseKeys from 'snakecase-keys'
// 接收数据时:snake_case → camelCase
const apiResponse = await fetch('/api/users')
const data = camelcaseKeys(await apiResponse.json(), { deep: true })
// 发送数据时:camelCase → snake_case
const body = snakecaseKeys(userData, { deep: true })
await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(body),
})觉知:常见问题
1. 循环引用
typescript
// ❌ 会报错
const obj: any = { name: 'Alice' }
obj.self = obj // 循环引用
JSON.stringify(obj) // TypeError: Converting circular structure to JSON2. 忘记设置 Content-Type
typescript
// ❌ 服务器可能无法正确解析
fetch('/api/users', {
method: 'POST',
body: JSON.stringify(data),
})
// ✅ 必须设置 Content-Type
fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})3. 数字精度丢失
typescript
// 大数字可能丢失精度
const bigNumber = 9007199254740993 // 超出 JS 安全整数范围
JSON.parse('{"id": 9007199254740993}')
// { id: 9007199254740992 } 精度丢失!
// 解决方案:用字符串传递大数字
{ "id": "9007199254740993" }本节小结
| 要点 | 说明 |
|---|---|
| JSON.stringify | 对象转字符串 |
| JSON.parse | 字符串转对象 |
| Date 处理 | 需要手动转换或用 superjson |
| Content-Type | 发送 JSON 必须设置 |
| 命名规范 | 前后端约定一致 |
