1. 介紹
我們已經開始接觸集合了。沒它們真不行,無論在程式設計還是日常生活。想像你在列一天的待辦事項:買菜、打電話給醫生、取包裹。你不會為每一件事都開一本小本子,對吧?把任務都放到同一個清單裡更簡單也更合理。
程式設計也是一樣:當物件很多——使用者、訂單、訊息——我們不會為每個建立單獨變數,而是用集合:清單、字典、集合(set)。這樣便於儲存、遍歷和一次處理一整組資料。
現實場景:
- 在應用之間儲存與交換資料:你的書籍集合可能會從一個程式遷移到另一個程式。
- 把資料快取到磁碟上。
- 透過網路傳遞資料(frontend ↔ backend)。
- 匯入/匯出資訊(比如你想把書匯出成 JSON!)。
面試時:如果有人問「你會怎麼序列化並儲存訂單清單?」——除了提到單一物件,你能說出如何序列化集合會很加分!
2. 序列化時集合如何工作
JSON 與集合:簡短的愛情故事
當你序列化一個普通物件時,它會變成像 { "field": value } 這樣的 JSON 物件。但如果序列化的是清單或陣列,就會得到 JSON 陣列 [ ... ]。
視覺化:
| C# | JSON |
|---|---|
|
|
|
|
|
|
主要魔法:對集合呼叫 JsonSerializer.Serialize(),它會自動把集合變成陣列!相反地用 Deserialize<List<T>>() 也會把 JSON 陣列還原成集合。
C# 集合與 JSON 陣列的對應
| C# 集合類型 | 範例 | JSON |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3. 範例:書籍陣列的序列化與反序列化
我們先從最小的範例開始。假設有我們之前講過的類別 Book:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
現在建立一個書籍陣列,序列化它,存到檔案,然後再讀回來。
using System;
using System.IO;
using System.Text.Json;
namespace LibraryApp
{
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
class Program
{
static void Main()
{
// Sozdaem massiv knig
Book[] books = new Book[]
{
new Book { Title = "Tri tovarishcha", Author = "Erikh Mariya Remark" },
new Book { Title = "Master i Margarita", Author = "Mikhail Bulgakov" },
new Book { Title = "1984", Author = "Dzhordzh Oruell" }
};
// Serializatsiya massiva knig v stroku
var options = new JsonSerializerOptions { WriteIndented = true };
string json = JsonSerializer.Serialize(books, options);
Console.WriteLine("JSON massiva knig:\n" + json);
// Zapic v fail (sinhrono)
File.WriteAllText("books.json", json);
// Chtenie iz faila (sinhrono)
string jsonFromFile = File.ReadAllText("books.json");
// Deserializatsiya obratno v massiv
Book[]? booksFromFile = JsonSerializer.Deserialize<Book[]>(jsonFromFile);
// Vivodim rezultat na konsol
Console.WriteLine("\nDeserializovannye knigi:");
if (booksFromFile != null)
{
foreach (var book in booksFromFile)
{
Console.WriteLine($"- {book.Title} (avtor: {book.Author})");
}
}
}
}
}
這裡發生了什麼?
- 我們建立了 Book[] 陣列,填入三本書。
- 用 JsonSerializer.Serialize 把陣列變成漂亮的 JSON 字串(選項 WriteIndented 讓格式可讀)。
- 寫入檔案、再讀出並反序列化——結果我們又得到了書籍陣列!
- 檢查所有資料是否正確還原。
確認檔案 "books.json" 出現在程式的資料夾。打開它,會大致看到這樣的 JSON:
[
{
"Title": "Tri tovarishcha",
"Author": "Erikh Mariya Remark"
},
{
"Title": "Master i Margarita",
"Author": "Mikhail Bulgakov"
},
{
"Title": "1984",
"Author": "Dzhordzh Oruell"
}
]
4. 範例:序列化 List<T> 集合
對 List<Book> 的處理沒什麼兩樣。序列化器會把集合當作 JSON 陣列來處理。
List<Book> myBooks = new List<Book>
{
new Book { Title = "Prestuplenie i nakazanie", Author = "Fyodor Dostoevskiy" },
new Book { Title = "Voina i mir", Author = "Lev Tolstoy" }
};
string jsonList = JsonSerializer.Serialize(myBooks, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonList);
// A deserializuem vot tak:
List<Book>? loadedBooks = JsonSerializer.Deserialize<List<Book>>(jsonList);
// Proveryaem, chto vsyo vernulos!
foreach (var book in loadedBooks!)
{
Console.WriteLine($"{book.Title} ({book.Author})");
}
可以只序列化整數清單嗎?
當然可以!序列化不只對你的類別有效,對簡單型別也一樣:
List<int> numbers = new List<int> { 10, 20, 30, 40 };
string jsonNums = JsonSerializer.Serialize(numbers); // rezultat: [10,20,30,40]
List<int>? loadedNums = JsonSerializer.Deserialize<List<int>>(jsonNums);
// loadedNums: List<int> s tem zhe znacheniyami
5. 架構與類比:序列化器如何看待集合?
為了更好理解集合的序列化,我們看個簡單的流程圖:
graph TD;
A[List[Book] in C#] -->|Serialize| B[JSON-array in string]
B -->|Write| C[File books.json]
C -->|Read| D[JSON string from file]
D -->|Deserialize| E[List[Book] in C#]
簡短描述流程:
- 集合或陣列被序列化為 JSON 陣列 ([ ... ])。
- 元素的順序會被保留(除非你用了特殊設定改變它)。
- 集合中的每個元素會被當成單獨物件序列化。
6. 集合序列化的注意事項
1. null 與空集合
如果集合是 null,預設情況下序列化器會在 JSON 中寫出 null。在商業邏輯中,空集合和 null 有時候不是同一回事,要注意區分!
如果集合是空的(new List<Book>()),那 JSON 會是 [] —— 空陣列。這在你想明確表示「沒有項目」時很有用。
2. 反序列化——順序很重要
陣列中元素的順序會被保留。所以如果你序列化時是某個順序,反序列化後也會是那個順序。
3. 集合裡的不同型別物件?
System.Text.Json 預設不支援多型(polymorphism)。換句話說,如果你有 List<Animal> 裡面放了 Dog 和 Cat(分別是繼承自 Animal 的類別),預設情況下無法從 JSON 恢復出每個元素的具體型別。我們以後會講到多型序列化,但對普通清單來說一切如常。
7. 常見錯誤與誤解
錯誤 #1:序列化集合時忘了有些物件有私有欄位
JsonSerializer 只會序列化公有屬性(且需要 getter/setter)。如果你的類別像下面這樣:
public class User
{
public string Login { get; set; }
private string Password { get; set; } // Ne budet serializovano!
}
Password 不會出現在 JSON,這對安全來說通常是好事。但如果你真的想儲存它,記得把屬性設為 public。
錯誤 #2:序列化含有 null 元素的集合
如果集合包含 null,比如 new List<Book> { null, book2 },序列化後第一個元素會是 null。反序列化時也會如此。例子 JSON 如下:
[null, { "Title": "Voina i mir", "Author": "Lev Tolstoy" }]
實務中這種情況不常見,但如果你的集合可能有「洞」,在業務邏輯裡要處理好。
錯誤 #3:反序列化到錯的型別
常見的打字錯誤:你序列化成一個清單,但反序列化時想成陣列(或相反)。如果型別相容(Book[] ↔ List<Book>)通常沒問題,但有時會出現你沒預期到的差異,像是想把字串陣列反序列化成物件時就會出錯。
GO TO FULL VERSION