CodeGym /課程 /C# SELF /序列化簡單集合: List<T>

序列化簡單集合: List<T>, T[]

C# SELF
等級 46 , 課堂 0
開放

1. 介紹

我們已經開始接觸集合了。沒它們真不行,無論在程式設計還是日常生活。想像你在列一天的待辦事項:買菜、打電話給醫生、取包裹。你不會為每一件事都開一本小本子,對吧?把任務都放到同一個清單裡更簡單也更合理。

程式設計也是一樣:當物件很多——使用者、訂單、訊息——我們不會為每個建立單獨變數,而是用集合:清單、字典、集合(set)。這樣便於儲存、遍歷和一次處理一整組資料。

現實場景

  • 在應用之間儲存與交換資料:你的書籍集合可能會從一個程式遷移到另一個程式。
  • 把資料快取到磁碟上。
  • 透過網路傳遞資料(frontend ↔ backend)。
  • 匯入/匯出資訊(比如你想把書匯出成 JSON!)。

面試時:如果有人問「你會怎麼序列化並儲存訂單清單?」——除了提到單一物件,你能說出如何序列化集合會很加分!

2. 序列化時集合如何工作

JSON 與集合:簡短的愛情故事

當你序列化一個普通物件時,它會變成像 { "field": value } 這樣的 JSON 物件。但如果序列化的是清單或陣列,就會得到 JSON 陣列 [ ... ]

視覺化:

C# JSON
List<int> { 1, 2, 3 }
[1, 2, 3]
Book[]
[{"Title":"A","Author":"B"}, ...]
List<Book>
[{"Title":"A"}, {"Title":"B"}]

主要魔法:對集合呼叫 JsonSerializer.Serialize(),它會自動把集合變成陣列!相反地用 Deserialize<List<T>>() 也會把 JSON 陣列還原成集合。

C# 集合與 JSON 陣列的對應

C# 集合類型 範例 JSON
Book[]
new Book[] { ... }
[ { ... }, { ... } ]
List<Book>
new List<Book> { ... }
[ { ... }, { ... } ]
int[]
new int[] { 1, 2, 3 }
[1, 2, 3]
List<string>
new List<string> { "a", "b" }
["a", "b"]
List<List<Book>>
new List<List<Book>> { ... }
[ [ { ... } ], [ { ... } ] ]

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>)通常沒問題,但有時會出現你沒預期到的差異,像是想把字串陣列反序列化成物件時就會出錯。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION