1. 為什麼需要選擇格式?簡短提醒
任何 .NET 物件在記憶體裡 — 都是地址、參考、欄位、內部結構和其他「機器裡的東西」的集合。但這樣直接存成檔案不行:檔案必須以一種結構化的格式保存,其他程式不只要能讀,最好也能寫。這時就出現了序列化格式的選擇。
序列化格式的主要任務:
- 緊湊性:磁碟空間或網路傳輸速度。
- 可讀性(對人!):能否在文字編輯器中打開並理解內容。
- 通用性:是否適合與其他系統和語言整合。
- 支援複雜結構:巢狀、集合、參考、型別。
- 安全性與相容性。
2. 二進位序列化 (Binary Serialization)
二進位序列化把資料表示成「原始」的位元組流。想像你不只是把東西放進箱子,而是把它們用水泥封住 — 快速、緊湊,但之後要取出自己的襪子可能得動用震動鑽。
// 示例僅作歷史參考:
using System.Runtime.Serialization.Formatters.Binary;
// ... 創建物件
var user = new User { Name = "伊萬", Age = 42 };
// 序列化
using var fs = new FileStream("user.bin", FileMode.Create);
var formatter = new BinaryFormatter();
formatter.Serialize(fs, user);
優點:
- 緊湊:佔用磁碟空間極少,速度高。
- 內建支援 .NET 的「原生」型別。
缺點:
- 格式完全不可讀 — 試著用記事本打開,會進入「矩陣」般的亂碼(就是不可讀)。
- 絕對不具通用性:只能用於 .NET(而且通常僅限於相同版本!)。
- 對版本間相容性非常脆弱。
以前哪裡用過?
在 .NET 內部,用於在同一台機器上的進程之間快速「dump」物件。現在強烈建議不要用,尤其如果你關心安全性和長期維護。現代專案中使用 BinaryFormatter 是很差的做法。
3. XML 格式 (Extensible Markup Language)
XML是一種人機皆可讀的標記格式,基於標籤,類似 HTML,但沒有那些關於 <body> 的花哨玩笑。
<User>
<Name>伊萬</Name>
<Age>42</Age>
</User>
序列化示例:
using System.Xml.Serialization;
var user = new User { Name = "伊萬", Age = 42 };
var serializer = new XmlSerializer(typeof(User));
using var fs = new FileStream("user.xml", FileMode.Create);
serializer.Serialize(fs, user);
優點:
- 可讀性:可以直接看出結構。
- 通用性:很多語言和程式都能讀 XML。
- 彈性(支援 schema、驗證、命名空間等)。
- 適合複雜結構和巢狀物件。
缺點:
- 冗長且笨重:佔空間多、傳輸成本高。
- 解析速度通常比二進位格式慢。
- 某些型別序列化時不夠精確(例如日期和時間)。
- 對某些集合或非標準物件需要額外設定。
應用場景?
企業系統之間的資料交換、配置檔、需要嚴格結構描述的整合場景。
4. JSON 格式 (JavaScript Object Notation)
JSON 是一種緊湊、輕量且易讀的格式,起源於 JavaScript 世界,憑藉其簡潔性和易用性幾乎稱霸了資料交換領域。
{
"Name": "伊萬",
"Age": 42
}
序列化示例:
using System.Text.Json;
var user = new User { Name = "伊萬", Age = 42 };
string json = JsonSerializer.Serialize(user);
File.WriteAllText("user.json", json);
優點:
- 對人和機器都非常可讀。
- 容易與網路技術整合(API、JavaScript 等)。
- 比 XML 更緊湊。
- 在現代庫中序列化/反序列化速度快(例如 System.Text.Json, Newtonsoft.Json)。
缺點:
- 不支援註解(你的 JSON 無法自嘲)。
- 結構上有限制:例如無法直接序列化物件間的參考或某些複雜型別(比如鍵不是字串的字典)。
- 不同實現之間的日期/時間格式可能會有差異。
應用場景?
幾乎所有需要跨平台資料交換的地方:網路服務、行動應用、REST API 等。
5. CSV (Comma-Separated Values)
CSV 是一種簡單的文字格式,把資料表示成表格(列與欄),欄位以逗號或其他符號分隔。
Name,Age
伊萬,42
奧爾嘉,27
手動寫入 CSV 的示例:
// 為簡單起見,通過普通 StreamWriter 寫入
var lines = new List<string>
{
"Name,Age",
"伊萬,42",
"奧爾嘉,27"
};
File.WriteAllLines("users.csv", lines);
優點:
- 被大量程式支援(Excel、Google Docs、資料庫等)。
- 簡潔,適合表格型資料。
缺點:
- 不適合巢狀或複雜物件(只能是「扁平」結構)。
- 不儲存型別資訊(全部都是字串)。
- 遇到逗號、引號、換行時需要轉義,容易出錯。
應用場景?
在資料庫之間匯出/匯入、交換簡單記錄集、從應用匯出資料用於分析。
6. 帶強型別契約的協定與格式
Protocol Buffers (protobuf) 和 MessagePack 是具有契約(schema)的二進位序列化格式,能使序列化更緊湊、更快。它們需要事先定義資料結構的 schema。
protobuf 的簡化示例:
不是 .NET 標準的一部分,需要套件 Google.Protobuf。
message User {
string name = 1;
int32 age = 2;
}
優點:
- 極致的速度和緊湊性。
- 跨平台(很多語言支援 protobuf)。
缺點:
- 學習和設定較複雜。
- 需要基於 schema 生成程式碼。
- 如果專案不是要處理每秒數百萬條訊息,很多時候用不到它的全部優勢。
應用場景?
高負載系統、微服務間的資料交換、遊戲、IoT 等。
7. 有用的細節
格式比較表
| 格式 | 可讀性 | 緊湊性 | 通用性 | 複雜物件 | .NET 9 標準 | 安全性 |
|---|---|---|---|---|---|---|
| Binary (.NET) | 否 | 是 | 否 | 是 | 否 | 否 |
| XML | 是 | 否 | 是 | 是 | 是 | 是 |
| JSON | 是 | 是 | 是 | 部分 | 是 | 是 |
| CSV | 是 | 是 | 是 | 否 | 否 | 是 |
| Protobuf | 否 | 最佳 | 是 | 是 | 否 | 是 |
如何為你的應用選擇序列化格式?
如果要保存複雜資料結構且你重視人工可讀性 — 用 XML 或 JSON。
如果需要在你控制的服務間快速傳送大量資料 — 考慮 protobuf 或 MessagePack。
如果是為了在 Excel 或資料庫中分析導出報表 — 用 CSV。
如果是在為服務或 CI/CD 寫配置檔 — YAML 通常很實用。
如果緊湊性、速度和物件間參考支援是關鍵 — 要找二進位格式,但要記得它們在安全性和相容性上的缺點。
8. 狡猾的細節和常見錯誤
如果你打算把資料存在二進位格式以為「安全隱秘」,記住:二進位 dump 並不等於安全。攻擊者仍有可能反解析那個位元流。需要時請使用加密。
常見問題:在處理文字格式(XML/JSON/CSV)時忘了編碼 — 使用 UTF-8。不是所有編輯器和系統都喜歡 BOM。
XML 看起來「老舊」,但對於需要嚴格契約(比如 XSD schema)的協定仍然很有用;JSON 則適合快速、靈活的交換。
在序列化複雜物件集合時,不是所有格式都能正確支援巢狀或遞迴結構。JSON 和 XML 對陣列與清單支持良好,而 CSV 則不支援。
GO TO FULL VERSION