CodeGym /课程 /C# SELF /简单集合的序列化: List<T>

简单集合的序列化: List<T>, T[]

C# SELF
第 46 级 , 课程 0
可用

1. 介绍

我们之前已经练过集合。没有它们编程和生活都不方便。想象你写一天的待办:买菜、打给医生、去取快递。你不会为每件事都准备一个单独的小本子。把所有任务放到一个列表里更简单、更合理。

编程也是一样:当对象变多——用户、订单、消息——我们通常不会为每个对象声明一个变量。相反用集合:列表、字典、集合。这样能方便地存储、遍历和处理整组数据。

现实场景举例

  • 在应用间存储和交换数据:你的书籍集合可以从一个程序迁移到另一个程序。
  • 把数据集缓存到磁盘上。
  • 通过网络传输数据(frontend ↔ backend)。
  • 导入/导出信息(比如你想把书导出成 JSON)。

面试问法:问你「你会怎么序列化并保存一个订单列表?」——如果你能说到集合的序列化而不只是单个对象,会很加分!

2. 集合在序列化时如何工作

JSON 和集合:一段短暂的恋爱史

当你序列化普通对象时,它会变成 JSON 对象,形如 { "field": value }。但如果序列化一个列表或数组,会得到 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>>() —— 魔法又回来了。

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()
        {
            // 创建一个书籍数组
            Book[] books = new Book[]
            {
                new Book { Title = "三位好友", Author = "埃里希·玛利亚·雷马克" },
                new Book { Title = "大师与玛格丽特", Author = "米哈伊尔·布尔加科夫" },
                new Book { Title = "1984", Author = "乔治·奥威尔" }
            };

            // 把书籍数组序列化成字符串
            var options = new JsonSerializerOptions { WriteIndented = true };	
            string json = JsonSerializer.Serialize(books, options);
            Console.WriteLine("JSON 的书籍数组:\\n" + json);

            // 同步写入文件
            File.WriteAllText("books.json", json);

            // 同步从文件读取
            string jsonFromFile = File.ReadAllText("books.json");

            // 反序列化回数组
            Book[]? booksFromFile = JsonSerializer.Deserialize<Book[]>(jsonFromFile);

            // 在控制台输出结果
            Console.WriteLine("\\n反序列化得到的书籍:");
            if (booksFromFile != null)
            {
                foreach (var book in booksFromFile)
                {
                    Console.WriteLine($"- {book.Title} (作者: {book.Author})");
                }
            }
        }
    }
}

这里发生了什么?

  • 我们创建了一个 Book[] 数组,填入三本书。
  • 通过 JsonSerializer.Serialize 把数组变成漂亮的 JSON 字符串(选项 WriteIndented 使其易读)。
  • 把字符串写入文件,再读回来并反序列化 —— 现在我们又得到了书籍数组!
  • 检查所有数据是否正确恢复。

确认一下文件 "books.json" 出现在程序目录下。打开它,你会看到类似这样的 JSON:

[
  {
    "Title": "三位好友",
    "Author": "埃里希·玛利亚·雷马克"
  },
  {
    "Title": "大师与玛格丽特",
    "Author": "米哈伊尔·布尔加科夫"
  },
  {
    "Title": "1984",
    "Author": "乔治·奥威尔"
  }
]

4. 示例:序列化集合 List<T>

List<Book> 的处理没有区别。序列化器看到集合就把它变成 JSON 数组。

List<Book> myBooks = new List<Book>
{
    new Book { Title = "罪与罚", Author = "陀思妥耶夫斯基" },
    new Book { Title = "战争与和平", Author = "列夫·托尔斯泰" }
};

string jsonList = JsonSerializer.Serialize(myBooks, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(jsonList);

// 反序列化如下:
List<Book>? loadedBooks = JsonSerializer.Deserialize<List<Book>>(jsonList);

// 检查数据都恢复了!
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); // 结果: [10,20,30,40]
List<int>? loadedNums = JsonSerializer.Deserialize<List<int>>(jsonNums);
// loadedNums: 包含相同值的 List<int>

5. 架构和类比:序列化器如何看待集合?

为了更好理解集合的序列化,看看这张简单的流程图:

graph TD;
    A[List[Book] 在 C#] -->|Serialize| B[JSON 数组字符串]
    B -->|Write| C[文件 books.json]
    C -->|Read| D[从文件读取的 JSON 字符串]
    D -->|Deserialize| E[List[Book] 在 C#]

简短描述流程:

  • 集合或数组会被序列化成 JSON 中的数组([ ... ])。
  • 元素顺序会被保留(除非你用特殊设置改变它)。
  • 集合中的每个元素会被单独序列化成对象。

6. 集合序列化的特性:注意点

1. null 与空集合

如果集合是 null,序列化器默认会把 null 写入 JSON。注意在业务逻辑中空集合和 null 可能并不相同!

如果集合是空的(new List<Book>()),JSON 会是 [] —— 空数组。这在你想明确表示没有元素时很方便。

2. 反序列化——顺序很重要

数组中元素的顺序总会被保留。也就是说如果你按某个顺序序列化三本书,反序列化后顺序也是一样的。

3. 集合里放不同类型的对象?

System.Text.Json 默认不支持多态。也就是说如果你有一个 List<Animal>,里面既有 Dog 又有 Cat(它们是派生类),默认情况下无法恢复出每个元素具体的派生类型。多态序列化我们以后会讲,但在普通列表里通常不需要额外处理。

7. 常见错误和误解

错误 №1:序列化集合时忘了集合里可能有私有字段的对象

JsonSerializer 只会序列化公共属性(并且要有 getter/setter)。比如如果你的类是这样的:

public class User
{
    public string Login { get; set; }

    private string Password { get; set; }  // 不会被序列化!
}

密码不会出现在 JSON 中,这对安全来说是好事。但如果你确实想保存它——别忘了把属性设为 public。

错误 №2:序列化包含 null 元素的集合

如果集合里有 null,比如:new List<Book> { null, book2 },序列化后第一个元素位置会是 null。反序列化时也是如此!示例 JSON:

[null, { "Title": "战争与和平", "Author": "列夫·托尔斯泰" }]

实际应用中这种情况不常见,但如果你的集合可能有“空洞”,在逻辑里要考虑到这一点。

错误 №3:反序列化成了错误的类型

常见笔误:你序列化了列表,但反序列化时用了数组(或反过来)。如果类型兼容(Book[]List<Book>)通常没问题,但有时候会出错,比如你想把字符串数组反序列化成对象而不是集合。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION