Skip to content

序列化:數據的"翻译"

🎯 核心問题

數據如何在網絡上傳輸? 這就像問:一个人說的话,如何讓另一个人听懂?序列化解决的就是"數據翻译"的問题——把內存中的對象翻译成可以傳輸的格式。


序列化數據的必要性

在前後端交互過程中,數據需要經歷多次"變形"才能從服務器傳遞到客户端。

場景一:前端收到的數據"變了"

javascript
// 後端發送
Date birth = new Date(1990, 5, 15)

// 前端收到
{ "birth": "1990-06-15T00:00:00Z" }  // 字符串!

前端想用 .getFullYear(),結果报錯了——因為這不是 Date 對象,是字符串。

場景二:中文亂碼

json
// 期望
{ "name": "张三" }

// 實际收到
{ "name": "å¼ ä¸" }

字符編碼問题導致中文變成亂碼。

場景三:性能瓶颈

json
// 一个包含 10000 條商品列表的響應
{
  "products": [
    { "id": 1, "name": "...", "description": "...", ... },
    // ... 9999 more
  ]
}
// 大小:5.2 MB,傳輸時間:3.5 秒

JSON 格式的冗餘導致數據包太大,嚴重影響性能。


序列化就像"翻译"——把內存對象"翻译"成可以傳輸的格式,接收方再"翻译"回去。


1. 什么是序列化/反序列化?

序列化(Serialization)就是把對象轉换成可傳輸格式的過程。

反序列化(Deserialization)就是把傳輸格式還原成對象的過程。

1.1 用寄快遞來類比

寄快遞序列化說明
打包物品序列化把物品装箱,贴上標簽
運輸網絡傳輸快遞車運送到目的地
拆包取物反序列化收件人打開箱子,取出物品

1.2 為什么需要序列化?

原因說明示例
網絡傳輸網絡只能傳輸字節流API 調用、RPC 通信
持久化存儲磁盘只能存儲字節保存對象到文件、數據庫
跨語言不同語言的數據結構不同Java 對象 → Python 字典
分布式緩存Redis/Memcached 存儲字節緩存用户信息

2. 常见的序列化格式

👇 動手試試看:點擊下方按钮,观察不同語言的序列化過程:

🔄Serialization Demo
📦In-memory object
const user = {
  id: 123,
  name: "Alice",
  email: "alice@example.com",
  age: 28
};
An object in memory, usable only by the current process
Serialize
{}JSON string68 bytes
{
  "id": 123,
  "name": "Alice",
  "email": "alice@example.com",
  "age": 28
}
Can be sent over the network and used across languages
Transfer
💻Binary52 bytes
Hex encoding (MessagePack):
\xa7 id 7b
\xa4 name \xa5 Alice
\xa5 email \xb1 alice@example.com
\xa3 age 1c
Protobuf/MessagePack, smaller and faster
📊 Format comparison
Format
Size
Speed
Readability
Cross-language
JSON
★★★☆☆
★★★☆☆
★★★★★
★★★★★
XML
★★☆☆☆
★★☆☆☆
★★★★★
★★★★★
Protobuf
★★★★★
★★★★★
★☆☆☆☆
★★★★☆
MessagePack
★★★★☆
★★★★☆
★★☆☆☆
★★★★★

2.1 JSON:最通用

優點

  • 可讀性好,調試方便
  • 所有語言都支持
  • 浏览器原生支持(JSON.parse / JSON.stringify

缺點

  • 體积大(有大量 {} "" 標記)
  • 不支持豐富的數據類型(Date、Map、Set 會被轉换成字符串)

適用場景

  • 公開 API
  • 前後端通信
  • 配置文件

2.2 XML:曾經的主流

xml
<?xml version="1.0" encoding="UTF-8"?>
<user>
  <id>123</id>
  <name>张三</name>
  <email>zhangsan@example.com</email>
  <age>28</age>
</user>

優點

  • 結構清晰,支持注釋
  • 支持複雜的嵌套結構
  • 有 Schema 验證(XSD)

缺點

  • 體积大,解析慢
  • 標簽冗餘(<open></close>

適用場景

  • 配置文件(Spring、MyBatis)
  • SOAP 協议
  • 複雜數據交换

2.3 Protobuf:最高效

protobuf
// user.proto
syntax = "proto3";
message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
  int32 age = 4;
}

優點

  • 體积小(比 JSON 小 30-50%)
  • 速度快(解析速度快 5-10 倍)
  • 向後兼容(新增字段不影響老版本)

缺點

  • 不可讀(二進制格式)
  • 需要 .proto 文件定義
  • 不支持動態類型

適用場景

  • 微服務內部通信
  • 高性能場景(游戏、實時通信)
  • 移動端 App(節省流量)

2.4 MessagePack:兼顧可讀性和性能

json
// MessagePack 是 JSON 的二進制版本
// 相同數據,MessagePack 比 JSON 小 30% 左右

優點

  • 比 JSON 小,比 JSON 快
  • 保持 JSON 的數據模型
  • 支持所有 JSON 類型

缺點

  • 不可讀
  • 不如 Protobuf 高效

適用場景

  • 需要性能但不想用 Protobuf
  • Redis 緩存
  • WebSocket 消息

3. 各語言序列化方式對比

語言JSON 庫Protobuf 庫XML 庫
JavaScriptJSON.stringify()protobuf.jsfast-xml-parser
Pythonjson.dumps()protobufxmltodict
JavaJackson / Gsonprotobuf-javaJAXB
Goencoding/jsonprotoencoding/xml
C++nlohmann/jsonprotobuftinyxml2
C#System.Text.JsonGoogle.ProtobufSystem.Xml

💡 選择建议

  • 前後端通信:JSON(調試方便)
  • 微服務內部:Protobuf(性能最優)
  • 配置文件:JSON 或 YAML
  • 舊系统對接:XML(可能別无選择)

4. 性能對比

4.1 大小對比(以用户對象為例)

格式大小相對 JSON
JSON68 bytes100%
XML142 bytes209%
Protobuf38 bytes56%
MessagePack52 bytes76%

4.2 速度對比(序列化 10000 次)

格式耗時相對 JSON
JSON45 ms100%
XML120 ms267%
Protobuf8 ms18%
MessagePack28 ms62%

💡 性能測試結论

  • Protobuf 最快:適合高性能場景
  • MessagePack 次之:比 JSON 快 40% 左右
  • JSON 最慢:但對大多數場景已經足够

5. 常见問题

5.1 日期序列化問题

問题:Date 對象序列化後變成字符串

javascript
// 序列化前
const date = new Date('2024-01-01')

// 序列化後
JSON.stringify(date)  // "2024-01-01T00:00:00.000Z"

解决方案

javascript
// 方案1:轉成時間戳
{ createdAt: date.getTime() }  // 1704067200000

// 方案2:轉成 ISO 字符串
{ createdAt: date.toISOString() }  // "2024-01-01T00:00:00.000Z"

// 方案3:自定義序列化
JSON.stringify(obj, (key, value) => {
  if (value instanceof Date) {
    return { __type: 'Date', value: value.toISOString() }
  }
  return value
})

5.2 循環引用問题

問题:對象循環引用會报錯

javascript
const obj = { name: 'test' }
obj.self = obj
JSON.stringify(obj)  // TypeError: Converting circular structure to JSON

解决方案

javascript
// 方案1:過滤掉循環引用
const seen = new WeakSet()
JSON.stringify(obj, (key, value) => {
  if (typeof value === 'object' && value !== null) {
    if (seen.has(value)) return
    seen.add(value)
  }
  return value
})

// 方案2:使用 flatted 庫
import { parse, stringify } from 'flatted'
stringify(obj)  // 自動處理循環引用

5.3 中文亂碼問题

問题:中文序列化後亂碼

原因

  • 字符編碼不一致(UTF-8 vs GBK)
  • BOM 標記

解决方案

python
# Python 确保使用 UTF-8
import json
json.dumps(data, ensure_ascii=False)  # 不轉義中文
javascript
// Node.js 設置響應頭
res.setHeader('Content-Type', 'application/json; charset=utf-8')

6. 實戰:電商系统序列化方案

6.1 場景分析

場景格式選择理由
App → 後端 APIJSON調試方便,前後端统一
後端 → 後端 RPCProtobuf性能最優,節省流量
緩存到 RedisMessagePack比 JSON 小,可序列化複雜對象
日志記錄JSON便于日志分析工具解析

6.2 代碼示例

javascript
// API 響應(JSON)
app.get('/api/products/:id', async (req, res) => {
  const product = await db.getProduct(req.params.id)
  res.json({
    code: 0,
    data: product
  })
})

// 微服務通信(Protobuf)
// product.proto
syntax = "proto3";
message Product {
  int32 id = 1;
  string name = 2;
  int32 price = 3;
}

// 服務端
const proto = require('./product.proto')
const message = proto.Product.create(product)
const buffer = proto.Product.encode(message).finish()

// 客户端
const decoded = proto.Product.decode(buffer)

// Redis 緩存(MessagePack)
const msgpack = require('msgpack-lite')
await redis.set(
  `product:${id}`,
  msgpack.encode(product)
)
const cached = msgpack.decode(await redis.get(`product:${id}`))

7. 用 AI 輔助選择序列化方案

AI 可以帮助你根據場景選择合適的序列化格式。

7.1 提示词模板

你是一位资深的系统架構师,精通數據序列化技術。請帮我選择合適的序列化方案。

## 業務場景
[描述你的場景,例如:電商 App、游戏後端、微服務等]

## 技術要求
[列出约束條件,例如:
- 前後端分離(Vue + Node.js)
- 性能要求高(QPS > 10000)
- 流量敏感(移動端,需節省流量)
- 需要跨語言(Java + Python + Go)]

## 數據特征
[描述數據特點,例如:
- 數據量大(單次響應 > 1MB)
- 結構複雜(多層嵌套)
- 包含日期、二進制數據等特殊類型]

## 輸出格式
請按以下格式輸出:
1. 推荐方案(JSON/Protobuf/MessagePack/XML)
2. 理由說明
3. 性能對比(估算大小和速度)
4. 代碼示例

7.2 實戰示例

輸入提示词

你是一位资深的系统架構师。我们的電商 App 需要優化商品列表接口性能。

## 業務場景
- 電商 App 的商品列表接口
- 單次返回 100 个商品
- 每个 商品包含:id, name, price, image, description 等 20 个字段
- 当前使用 JSON,響應大小约 200KB,耗時 800ms

## 技術要求
- 前端是 Vue.js
- 後端是 Node.js + Go 微服務
- 需要兼顧性能和開發效率

## 優化目標
- 響應時間降到 300ms 以內
- 數據包大小减少 30%

AI 輸出

推荐方案:混合方案

1. **API 層**:继續使用 JSON(前端友好)
2. **微服務通信**:切换到 Protobuf(性能提升)
3. **静態數據**:启用 gzip 压缩

### 理由說明
- JSON 對前端最友好,无需额外解析
- 後端微服務用 Protobuf,可以减少 40% 的數據量
- gzip 压缩對文本效果明顯(JSON 可压缩 70%)

### 性能對比
| 方案 | 大小 | 耗時 |
|------|------|------|
| 当前 (JSON) | 200 KB | 800 ms |
| + gzip | 60 KB | 350 ms |
| + Protobuf | 50 KB | 280 ms |

### 代碼示例
[具體實現代碼...]

名词速查表

名词英文解釋
序列化Serialization對象 → 字節流
反序列化Deserialization字節流 → 對象
JSONJavaScript Object Notation最常用的文本格式
XMLExtensible Markup Language標記語言,曾主流
ProtobufProtocol BuffersGoogle 開源的高效格式
MessagePack-JSON 的二進制版本
編碼Encoding字符 → 字節
解碼Decoding字節 → 字符