1. 介紹
XML 是 eXtensible Markup Language 的縮寫。想像一個非常嚴謹又愛說話的 JSON,不停跟你說「我很有結構,相信我!」。XML 是一種以標籤包裹資料的純文字格式:
<Player>
<Name>Aragorn</Name>
<Health>100</Health>
</Player>
為什麼現在還要把物件序列化成 XML?
- XML 在大型系統整合(常見於「legacy」系統)裡仍被廣泛使用,特別是需要結構與基於嚴格 schema(XSD)的驗證時。
- 在企業設定上也常見 XML(例如舊版 .NET 的 App.config 和 Web.config)。
- 當你需要資料的「自我描述性」時,XML 很適合這種場景。
有趣的事:XML 最初設計時就是要盡可能地「可擴充」和可驗證;名字就是從這來的。它可以用 schema(例如 XSD)做驗證,這是 JSON 預設沒辦法做到的。
XmlSerializer:主角
在 .NET 中負責把物件序列化成 XML 的類別是 System.Xml.Serialization.XmlSerializer。
- 它會把公開的(public)屬性和欄位轉成 XML 元素,反之亦然:由 XML 還原物件。
- 只對公開的型別與成員有效。
- 不會序列化 private 的欄位與屬性 — 只有公開的會被處理。
- 序列化的類別需要有一個無參數的建構子(constructor)。
注意:如果你的類別不滿足這些條件,XmlSerializer 就無法工作,會在序列化或反序列化時丟出例外。
2. 簡單範例 — 將物件序列化成 XML
我們來看怎麼把我們常用的 Player 類序列化成 XML。
步驟 1. 定義類別
public class Player
{
public string Name { get; set; }
public int Health { get; set; }
public bool IsAlive { get; set; }
// 重要:需要一個無參數的公共建構子(constructor)!
public Player() { }
// 可選:方便起見
public Player(string name, int health, bool isAlive)
{
Name = name;
Health = health;
IsAlive = isAlive;
}
}
步驟 2. 序列化到檔案
using System.Xml.Serialization;
Player aragorn = new Player("Aragorn", 100, true);
// 1. 為 Player 型別建立 XmlSerializer
XmlSerializer serializer = new XmlSerializer(typeof(Player));
// 2. 開檔寫入(檔案不存在會自動建立)
using FileStream fs = new FileStream("player.xml", FileMode.Create);
// 3. 將物件序列化成 XML
serializer.Serialize(fs, aragorn);
結果 (player.xml):
<?xml version="1.0"?>
<Player xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Aragorn</Name>
<Health>100</Health>
<IsAlive>true</IsAlive>
</Player>
最簡流程視覺示意
記憶體中的物件 ---> XmlSerializer.Serialize() ---> 磁碟上的 XML 檔案
3. 反序列化 — 從 XML 把物件「喚回」
當你要讀回先前序列化的物件,流程是反過來:
using FileStream fs = new FileStream("player.xml", FileMode.Open);
XmlSerializer serializer = new XmlSerializer(typeof(Player));
Player player = (Player)serializer.Deserialize(fs);
Console.WriteLine($"姓名: {player.Name}, 生命值: {player.Health}, 存活: {player.IsAlive}");
結果程式會輸出: 姓名: Aragorn, 生命值: 100, 存活: True
4. 序列化成字串 — 不只寫檔案
序列化成字串
有時你不想把東西寫成檔案,可能要把 XML 當字串傳網路或顯示在畫面上。
using System.IO;
using System.Xml.Serialization;
Player player = new Player("Frodo", 42, true);
XmlSerializer serializer = new XmlSerializer(typeof(Player));
StringWriter stringWriter = new StringWriter();
serializer.Serialize(stringWriter, player);
string xmlString = stringWriter.ToString();
Console.WriteLine(xmlString); // 整段 XML 字串會輸出在螢幕上!
從字串反序列化
同樣地,你也可以從字串而不是檔案反序列化物件:
string xml = "<Player Name=\"Frodo\"><HP>42</HP></Player>";
XmlSerializer serializer = new XmlSerializer(typeof(Player));
using StringReader stringReader = new StringReader(xml);
Player frodo = (Player)serializer.Deserialize(stringReader);
Console.WriteLine(frodo.Name); // Frodo
5. XML 客製化:用屬性控制結構
如果預設行為不合你胃口,像是 XML 太冗長或標籤名稱不對,這時可以靠幾個「魔法」屬性直接告訴序列化器該怎麼處理類別成員。
常用的:
— 指定元素名稱。[XmlElement]
— 把欄位當成 XML attribute(屬性),而不是 element(元素)。[XmlAttribute]
— 從序列化中排除該屬性。[XmlIgnore]
和[XmlArray]
— 控制集合的包裝(wrapper)和項目名稱。[XmlArrayItem]
使用範例:
public class Player
{
[XmlAttribute]
public string Name { get; set; }
[XmlElement("HP")]
public int Health { get; set; }
[XmlIgnore]
public bool IsTempSessionPlayer { get; set; }
}
輸出 XML:
<Player Name="Aragorn">
<HP>100</HP>
</Player>
序列化主要屬性表
| 屬性 | 影響 | 範例 |
|---|---|---|
|
把屬性變成 XML attribute | |
|
改變標籤名稱 | 代替 |
|
完全從序列化中排除該屬性 | — |
|
改變集合「包裹」名稱 | |
|
改變集合中每個項目的名稱 | |
集合的進階序列化範例:
public class GameWorld
{
[XmlArray("Heroes")]
[XmlArrayItem("Hero")]
public List<Player> Players { get; set; } = new List<Player>();
}
XML:
<GameWorld>
<Heroes>
<Hero Name="Legolas">
<HP>90</HP>
</Hero>
<!-- ... -->
</Heroes>
</GameWorld>
關於 XML 的進階客製化,我們會在後面的講義再講! :P
GO TO FULL VERSION