1. 介紹
提醒:序列化是把物件的狀態保存為字串、檔案或串流的方式,之後可以還原該物件(反序列化)——你就會得到一個包含相同資料的物件。
典型情況:
1) 你有一個物件,例如一個有名字和年齡的使用者。
2) 需要把它保存——傳輸到網路、寫入檔案或轉成 JSON 格式。
3) 然後從字串/檔案把資料取回來——又得到一個物件!
在 C# 裡這可以只用幾行程式完成。來看看具體怎麼做。
我們的應用程式
我們在做一個簡單的「聯絡人管理器」。假設有一個類別 Person,我們要序列化它。
public class Person
{
// 公開屬性是序列化所需要的!
public string Name { get; set; }
public int Age { get; set; }
// 無參數建構子對 XmlSerializer 是必要的
public Person() { }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
如果你還不熟悉這種語法別擔心——課程後面會詳細講解類別和屬性。現在先把它當作實驗用的資料模型。注意:公開屬性 Name 和 Age 對序列化很重要,而 XmlSerializer 需要公開的無參數建構子。
2. 用 System.Text.Json 做 JSON 序列化
最簡單的範例
說到做到!這是把物件序列化成 JSON 的範例:
using System;
using System.Text.Json;
Person person = new Person("阿麗莎", 28);
// 序列化:把物件轉成 JSON 字串
string json = JsonSerializer.Serialize(person);
Console.WriteLine("JSON:");
Console.WriteLine(json);
畫面會顯示什麼?
JSON:
{"Name":"阿麗莎","Age":28}
很簡單:物件 → JSON。這就是魔法!
反序列化 — 還原回來
現在做反過來的操作——從 JSON 字串還原物件:
string json = "{\"Name\":\"鮑伯\",\"Age\":35}";
// 反序列化:從 JSON 字串還原出 Person 類型的物件
Person restored = JsonSerializer.Deserialize<Person>(json);
Console.WriteLine("還原的物件:");
Console.WriteLine($"名字: {restored.Name}, 年齡: {restored.Age}");
結果:
還原的物件:
名字: 鮑伯, 年齡: 35
注意:如果資料結構不匹配(例如沒有某個屬性),對應欄位會保留預設值(參考型別為 null,數字為 0,等等)。
3. 用 XmlSerializer 做 XML 序列化/反序列化
XML 序列化 — 初探
using System;
using System.IO;
using System.Xml.Serialization;
Person person = new Person("卡蒂亞", 22);
// 為 Person 類型建立序列化器
var serializer = new XmlSerializer(typeof(Person));
// 將 XML 寫入檔案
using (var stream = new FileStream("person.xml", FileMode.Create))
{
serializer.Serialize(stream, person);
// using 區塊結束時串流會自動關閉
}
Console.WriteLine("XML 檔案已建立!");
在 person.xml 檔案中會看到類似以下內容:
<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>卡蒂亞</Name>
<Age>22</Age>
</Person>
重要一點:
寫入檔案時使用的是串流(Stream,例如 FileStream)。如果想要序列化成字串(例如要透過網路傳送),可以用 StringWriter:
var serializer = new XmlSerializer(typeof(Person));
using (var sw = new StringWriter())
{
serializer.Serialize(sw, person);
string xmlString = sw.ToString();
Console.WriteLine(xmlString);
}
從 XML 反序列化
using (var stream = new FileStream("person.xml", FileMode.Open))
{
// 記得轉型到需要的類型!
Person restored = (Person)serializer.Deserialize(stream);
Console.WriteLine($"名字: {restored.Name}, 年齡: {restored.Age}");
}
再提醒一次:XmlSerializer 要求被序列化的類別有公開的無參數建構子(constructor)。
4. 跟檔案與字串打交道:要選哪個?
在真實場景中,序列化可能直接成為字串(用於網路傳輸/存 DB)或成為檔案(用於長期儲存)。
序列化成字串(例如用於 API 傳輸):
- JSON:使用 JsonSerializer.Serialize(obj)。
- XML:透過 StringWriter。
序列化到檔案(例如導出/備份):
- JSON:把字串用一般檔案寫入方法存檔。
- XML:直接用 XmlSerializer 寫入串流。
範例:序列化到檔案並還原(JSON)
using System.IO;
using System.Text.Json;
Person person = new Person("湯姆", 42);
// 序列化成字串
string json = JsonSerializer.Serialize(person);
// 寫入檔案
File.WriteAllText("contact.json", json);
// 從檔案讀取
string readJson = File.ReadAllText("contact.json");
Person restored = JsonSerializer.Deserialize<Person>(readJson);
Console.WriteLine($"{restored.Name}, 年齡: {restored.Age}");
5. JSON vs XML 的比較
流程視覺化
為了不搞混,這是序列化與反序列化的流程圖:
flowchart LR
A[物件] -- 序列化 --> B[字串/檔案/串流]
B -- 反序列化 --> C[物件]
A -. JSON 或 XML .-> B
B -. JSON 或 XML .-> C
可以把序列化想像成「把行李打包」,反序列化就是「回到家把行李打開」。
比較表
| 功能 | JSON (System.Text.Json) | XML (XmlSerializer) |
|---|---|---|
| 讀/寫字串 | ++ | + |
| 讀/寫檔案 | ++ | ++ |
| 是否需要無參數建構子 | 否 | 是 |
| 可讀性(對人類) | ++ | + |
| 嚴格的資料結構(驗證) | - | ++ |
| 彈性(自定義屬性) | + | ++ |
| 效能 | ++ | + |
| 網路相容性 | ++ | + |
++ — 非常好,+ — 普通,- — 不支援
6. 如何在我們的應用加入序列化功能
假設「聯絡人管理器」需要把使用者的聯絡人清單存成檔案,並能恢復它。
情境:我們有一個聯絡人清單(List<Person>)。
List<Person> contacts = new List<Person>
{
new Person("瓦西亞", 30),
new Person("彼得", 25),
new Person("瑪莎", 27)
};
// 儲存所有聯絡人 — 序列化成 JSON
string jsonContacts = JsonSerializer.Serialize(contacts);
File.WriteAllText("contacts.json", jsonContacts);
// 還原聯絡人清單
string readContactsJson = File.ReadAllText("contacts.json");
List<Person> restored = JsonSerializer.Deserialize<List<Person>>(readContactsJson);
foreach (var c in restored)
{
Console.WriteLine($"{c.Name}, 年齡: {c.Age}");
}
對 XML 也可以做同樣的事,但有些細節要注意。用 XmlSerializer 序列化集合時,通常會把清單包在一個容器類別(根元素)裡——這樣比較容易控制格式與元素名稱。
7. 序列化與反序列化的常見錯誤
錯誤 #1:序列化與反序列化時類型不一致。
把 List<Person> 序列化後,反序列化時也要用 List<Person>,而不是 List<object> 或 IEnumerable<Person>。否則會得到錯誤或意料外的結果。
錯誤 #2:嘗試序列化非公開欄位或屬性。
常見序列化器(JsonSerializer、XmlSerializer)只會處理公開的成員。如果屬性沒有公開的 getter/setter,值可能不會進到 JSON/XML,你就會納悶為什麼是 null。
錯誤 #3:缺少無參數建構子。
對於 XmlSerializer,公開的無參數建構子是必需的。若沒有,反序列化會拋出例外。
錯誤 #4:對物件參考的誤解。
如果一個物件持有對另一個物件的引用,序列化會以「值」的方式序列化(如巢狀物件)。引用本身不會被保留,可能導致資料重複或在有循環引用時出問題。
錯誤 #5:混用序列化格式。
XML 和 JSON 是不同的格式、不同的序列化器。不能把 JSON 字串傳給 XmlSerializer 或反過來——會出錯。
GO TO FULL VERSION