[技術探討] XML序列化 (Serialize)與反序列化(Deserialize) – 上篇

XML序列化

什麼是序列化? 可能許多開發人員曾沒聽過或沒使用過,序列化是將物件狀態轉換為可保存或可傳輸格式的處理序。而什麼是反序列化? 也就是序列化的反面,就是在還原序列化,它可以將資料流轉換成物件。說到這裡可能還是有些人不太懂也覺得很抽象,簡單來說,序列化的目的就是讓資料易於儲存和傳輸,是將一個物件的公用屬性和欄位轉換成XML格式或其他資料流傳輸格式,反序列化則是還原為物件,以易於程式取得物件資料與處理。若還不清楚,可直接看以下示範例子:

如何序列化XML

示範如何利用類別公用欄位和屬性將資料儲存為*.xml檔案:

1.定義欲序列化物件的公用欄位和屬性。

XML序列化

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內容

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);
  1. 序列化處理類別時,加入「Serializable」屬性就可被序列化,公開成員可省略不寫Serializable即可被序列化。
  2. 使用「XmlIgnore」屬性可不被序列化,可觀察到Person類別中有個公開名為Name的屬性,但是該屬性有加上「XmlIgnore」,執行序列化Xml後的內容沒有該資料。
  3. 序列化類別必須要有建構式,否則無法進行序列化,顯示錯誤訊息為沒有參數建構函式。

若於Person類別增加Birthday屬性程式碼如下:

  • Person類別增加Birthday屬性

[XmlAttribute("Birthday")]
public DateTime? Birthday { get; set; }

 

竟發生下圖的執行錯誤訊息,原因是Xml序列化過程中DateTime不可為空字串或含空白值,需經過處理才可正常運作。

XML序列化

調整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序列化

Xml序列化應用的地方很多也很好用,使用過後會發現是儲存和物件處理的不錯選擇,希望大家都能透過本篇更進一步多了解序列化和反序列化的使用。

MSDN參考資料

XML 和 SOAP 序列化

控制 XML 序列化的屬性

 

相關資料

[C#.NET] 利用 泛型方法 重構 反序列化

Force XmlSerializer to serialize DateTime as ‘YYYY-MM-DD hh:mm:ss’

字符串”2013/5/18 0:00:00”不是有效的 AllXsd 值

Comments

No comments yet. Why don’t you start the discussion?

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料