什麼是序列化? 可能許多開發人員曾沒聽過或沒使用過,序列化是將物件狀態轉換為可保存或可傳輸格式的處理序。而什麼是反序列化? 也就是序列化的反面,就是在還原序列化,它可以將資料流轉換成物件。說到這裡可能還是有些人不太懂也覺得很抽象,簡單來說,序列化的目的就是讓資料易於儲存和傳輸,是將一個物件的公用屬性和欄位轉換成XML格式或其他資料流傳輸格式,反序列化則是還原為物件,以易於程式取得物件資料與處理。若還不清楚,可直接看以下示範例子:
如何序列化XML
示範如何利用類別公用欄位和屬性將資料儲存為*.xml檔案:
1.定義欲序列化物件的公用欄位和屬性。
2.使用XmlSerializer並呼叫 Serialize 方法來產生 XML檔案。
- 序列化程式碼
using System.Xml; using System.Xml.Serialization; using System.IO; public static class XmlSerialize { /// <summary> /// 將類別序列化並產生XML檔案 /// </summary> /// <param name="fileName">建立XML檔案目標路徑</param> /// <param name="obj"></param> public static void SerializeToXml(string fileName, object obj) { Stream stream = null; StreamWriter writer = null; try { XmlSerializer xml = new XmlSerializer(obj.GetType()); stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read); writer = new StreamWriter(stream, Encoding.UTF8); xml.Serialize(writer, obj); } catch (Exception ex) { throw ex; } finally { writer.Close(); stream.Close(); } } } 示範用法 Person p = new Person(); p.Name = "M.K."; p.Phone = "0950123456"; XmlSerialize.SerializeToXml(AppDomain.CurrentDomain.BaseDirectory + "person.xml", p);
- XML序列化結果
如何泛型反序列化XML
反序列化的物件型別可能有很多種,這裡直接利用泛型方法回傳該物件的型別,以下為示範如何利用反序列化取得*.xml檔案內容:
- 反序列化程式碼
/// <summary> /// 反序列化XML檔案內容 /// </summary> /// <param name="FileName">XML檔案路徑</param> public static T DeserializeFromXml<T>(string FileName) { Stream stream = null; StreamReader reader = null; try { XmlSerializer xml = new XmlSerializer(typeof(T)); stream = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.None); reader = new StreamReader(stream, Encoding.UTF8); object obj = xml.Deserialize(reader); return (T)obj; } catch (Exception ex) { return default(T); } finally { stream.Close(); reader.Close(); } }
在這裡的default(T)表示為該物件的預設值,default預設關鍵字的說明可查看MSDN – 泛型程式碼中的預設關鍵字 (C# 程式設計手冊) ,在反序列化時並不是所有的物件都能反序列化,當反序列化失敗,則回傳該物件的預設值。
- 示範用法
Person person = XmlSerialize.DeserializeFromXml<Person>(AppDomain.CurrentDomain.BaseDirectory + "person.xml");
string result = string.Format("名字:{0},手機:{1}", person.Name, person.Phone);
// 執行結果result = 名字:M.K.,手機:0950123456
如何定義欲序列化的類別
介紹到這裡對於基本的序列化和反序列化功能也有所了解了,上述示範的物件類別只要公開欄位和屬性,沒有特別自訂控制序列化的欄位和屬性,序列化過程中系統會預設忽略private和protected成員,將每個公開成員都自動被序列化成xml element。可是有時候公開的資料必不想要被序列化,又該如何處理? 示範自訂序列化類別的程式碼如下:
- 預期執行結果的Xml內容
- 定義欲序列化類別
[Serializable, XmlRoot("ContactList")] public class ContactList { List<Person> pvContactList; [XmlElement("Person")] public List<Person> ContactLists { get { return pvContactList; } set { pvContactList = value; } } } [XmlRoot("Person")] public class Person { string firstName = string.Empty; string lastName = string.Empty; public Person() { pvAddrList = new List<Address>(); } public Person(string firstName, string lastName) { this.firstName = firstName; this.lastName = lastName; pvAddrList = new List<Address>(); } [XmlAttribute("FirstName")] public string FirstName { get { return firstName; } set { pvName = value + " " + LastName; firstName = value; } } [XmlAttribute("LastName")] public string LastName { get { return lastName; } set { pvName = FirstName + " " + value; lastName = value; } } string pvName = string.Empty; [XmlIgnore] public string Name { get { if (string.IsNullOrEmpty(pvName)) pvName = FirstName + " " + LastName; return pvName; } } string phone = string.Empty; [XmlAttribute("Phone")] public string Phone { get { return phone; } set { phone = value; } } List<Address> pvAddrList; [XmlElement("Address")] public List<Address> AddrList { get { return pvAddrList; } set { pvAddrList = value; } } } [XmlRoot("Address")] public class Address { [XmlElement] public string City { get; set; } [XmlElement("ZipCode")] public int ZipCode { get; set; } [XmlElement("BillsAddress")] public string BillsAddress { get; set; } }
- 示範用法
Person p = new Person("Steve","Jobs"); p.Phone = "0950123456"; List<Address> addrlist = new List<Address>(); addrlist.Add(new Address { City = "Taipei", ZipCode = 11575, BillsAddress = "台北市南港區忠孝東路六段21號10樓之2" }); addrlist.Add(new Address { City = "Taipei", ZipCode = 11575, BillsAddress = "台北市南港區忠孝東路五段815號8樓" }); p.AddrList = addrlist; ContactList list = new ContactList(); list.ContactLists = new List<Person> { p }; XmlSerialize.SerializeToXml(AppDomain.CurrentDomain.BaseDirectory + "person.xml", list);
- 序列化處理類別時,加入「Serializable」屬性就可被序列化,公開成員可省略不寫Serializable即可被序列化。
- 使用「XmlIgnore」屬性可不被序列化,可觀察到Person類別中有個公開名為Name的屬性,但是該屬性有加上「XmlIgnore」,執行序列化Xml後的內容沒有該資料。
- 序列化類別必須要有建構式,否則無法進行序列化,顯示錯誤訊息為沒有參數建構函式。
若於Person類別增加Birthday屬性程式碼如下:
- Person類別增加Birthday屬性
[XmlAttribute("Birthday")] public DateTime? Birthday { get; set; }
竟發生下圖的執行錯誤訊息,原因是Xml序列化過程中DateTime不可為空字串或含空白值,需經過處理才可正常運作。
調整DateTime屬性寫法,與給p.Birthday值:
- 序列化DateTime正確屬性寫法
[XmlIgnore] public DateTime? Birthday { get; set; } [XmlAttribute("Birthday")] public string StrBirthday { get { return Birthday.HasValue ? Birthday.Value.ToString("yyyy/MM/dd") : null; } set { Birthday = DateTime.Parse(value); } }
執行結果 – Birthday可正常顯示
Xml序列化應用的地方很多也很好用,使用過後會發現是儲存和物件處理的不錯選擇,希望大家都能透過本篇更進一步多了解序列化和反序列化的使用。
MSDN參考資料
相關資料
[C#.NET] 利用 泛型方法 重構 反序列化
Force XmlSerializer to serialize DateTime as ‘YYYY-MM-DD hh:mm:ss’