在分散式應用開發中,序列化是核心基礎設施之一。簡單來說,序列化是將物件的狀態轉換為可儲存或可傳輸的位元組序列,反序列化則是將位元組序列還原為物件。這主要服務於兩大場景:
.NET Framework 針對不同通訊協定與場景,提供了三大類序列化器。本文將逐一剖析 BinaryFormatter、SoapFormatter 以及 XmlSerializer 的運作原理、適用場景與程式碼實作。
這兩個序列化器皆實作 IRemotingFormatter 介面,專為 .NET Remoting 設計。Remoting 支援 TCP 與 HTTP 通道,BinaryFormatter 用於 TCP(高效能、二進位),SoapFormatter 用於 HTTP(XML 格式、穿透防火牆)。兩者具有強烈的 .NET 平台特徵,序列化資料中包含完整型別資訊(完整型別名稱、版本、文化特性、公開金鑰 Token),並具備型別保真特性:所有欄位(含私有欄位)皆會序列化,除非標記 [NonSerialized];屬性不序列化(因屬性僅為存取器)。
命名空間:System.Runtime.Serialization.Formatters.Soap
組件:system.runtime.serialization.formatters.soap.dll
[Serializable]
public class Book
{
private string name;
private string address1;
private string address2;
[NonSerialized]
private string address_invisible = "shenzhen";
public string author;
public string Name { get { return name; } set { name = value; } }
public Book() { address1 = address2 = "shenzhen"; }
}
重點:類別必須標記 [Serializable],私有欄位預設會被序列化,[NonSerialized] 可排除特定欄位。
MemoryStream stream = new MemoryStream();
Book book = new Book { Name = "我的第一本書", author = "chnking" };
SoapFormatter formatter = new SoapFormatter();
formatter.Serialize(stream, book);
// 注意:Serialize 後 stream.Position 在尾端,讀取前需歸零
stream.Position = 0;
byte[] data = stream.GetBuffer();
string xml = Encoding.UTF8.GetString(data); // SoapFormatter 預設 UTF-8
產生的 XML 包含完整型別資訊(組件名、版本、公開金鑰 Token)、所有欄位值,且同一物件參考(address1、address2 指向同一字串)會以 ID 參考方式保留,避免重複序列化。標記 [NonSerialized] 的欄位不會出現。

stream.Position = 0;
SoapFormatter formatter = new SoapFormatter();
Book restored = (Book)formatter.Deserialize(stream);
// address_invisible 因未序列化,反序列化後為 null
命名空間:System.Runtime.Serialization.Formatters.Binary
組件:mscorlib.dll
用法與 SoapFormatter 幾乎相同,差異在於輸出為緊湊二進位格式,效能最高、體積最小,但不可讀、跨平台性差。序列化結果中同樣包含型別資訊與物件參考關係。
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, book);
// 二進位資料夾雜字元,仍以 UTF-8 解碼檢視可見型別資訊

XmlSerializer 是 ASP.NET Web Services 預設序列化器,遵循 XML Schema 標準,輸出不含 .NET 型別資訊,僅序列化公有欄位與可讀寫屬性,不支援私有欄位、唯讀屬性、方法、索引子。這意味著它不具備型別保真,反序列化後的物件可能與原物件不同(私有狀態遺失),但適合異構系統整合。
命名空間:System.Xml.Serialization
組件:system.xml.dll
透過 XmlRootAttribute、XmlAttribute、XmlElementAttribute 等屬性,可精確控制根元素名稱、命名空間、成員呈現為屬性或元素、XML Schema 資料型別、Null 值處理等。
[XmlRoot(ElementName = "BOOK", Namespace = "https://www.chnking.com", IsNullable = true)]
public class Book
{
private string name;
public string address1;
public string address2;
[XmlAttribute(AttributeName = "Author", Namespace = "https://www.chnkingAttr.com", DataType = "NMTOKEN")]
public string author;
[XmlElement(ElementName = "NamePro", Namespace = "https://www.chnkingName.com", DataType = "NMTOKEN", IsNullable = true)]
public string Name { get { return name; } set { name = value; } }
public Book() { address1 = address2 = "shenzhen"; }
}
範例中 author 序列化為屬性,Name 為元素,DataType="NMTOKEN" 會將不合法字元(如 ^)轉為十六進位 ASCII(_x005E_)。
XmlSerializer serializer = new XmlSerializer(typeof(Book));
serializer.Serialize(stream, book); // 需傳入型別
// 反序列化
stream.Position = 0;
Book restored = (Book)serializer.Deserialize(stream);

觀察重點:address1、address2 皆完整輸出值(非參考),因 XmlSerializer 面向資料而非物件圖;author 屬性值 ^chnking 中的 ^ 被轉義。
建立同樣回傳 Book 的 Web Service,瀏覽器呼叫後輸出的 XML 與 XmlSerializer 直接序列化結果完全一致,證明兩者共用同一序列化引擎。

當雙方預先約定 Schema 時,可用 xsd.exe 由 XSD 產生符合該結構的 .NET 類別:
xsd test2.xsd test1.xsd test.xsd /c
產生的類別自動包含對應的序列化屬性,序列化後的 XML 符合原始 XSD 定義。

| 序列化器 | 適用場景 | 輸出格式 | 型別保真 | 跨平台 |
|---|---|---|---|---|
| BinaryFormatter | .NET Remoting (TCP)、高效能內部通訊 | 二進位 | 是 | 否 |
| SoapFormatter | .NET Remoting (HTTP)、需 XML 可讀性 | SOAP XML | 是 | 部分 |
| XmlSerializer | ASP.NET Web Services、異構系統整合 | 標準 XML | 否 | 是 |
下一篇將繼續探討 WCF 中的 DataContractSerializer 與序列化進階主題。