1. 程式人生 > >C#對象序列化與反序列化

C#對象序列化與反序列化

space ros 個人信息 瀏覽器 特性 點名 文件名 屬性節點 派生

1.對象序列化的介紹

(1).NET支持對象序列化的幾種方式

二進制序列化:對象序列化之後是二進制形式的,通過BinaryFormatter類來實現的,這個類位於System.Runtime.Serialization.Formatters.Binary命名空間下。

SOAP序列化:對象序列化之後的結果符合SOAP協議,也就是可以通過SOAP 協議傳輸,通過System.Runtime.Serialization.Formatters.Soap命名空間下的SoapFormatter類來實現的。

XML序列化:對象序列化之後的結果是XML形式的,通過XmlSerializer 類來實現的,這個類位於System.Xml.Serialization命名空間下。XML序列化不能序列化私有數據。

(2)幾種序列化的區別

二進制格式和SOAP格式可序列化一個類型的所有可序列化字段,不管它是公共字段還是私有字段。XML格式僅能序列化公共字段或擁有公共屬性的私有字段,未通過屬性公開的私有字段將被忽略。

使用二進制格式序列化時,它不僅是將對象的字段數據進行持久化,也持久化每個類型的完全限定名稱和定義程序集的完整名稱(包括包稱、版本、公鑰標記、區域性),這些數據使得在進行二進制格式反序列化時亦會進行類型檢查。SOAP格式序列化通過使用XML命名空間來持久化原始程序集信息。而XML格式序列化不會保存完整的類型名稱或程序集信息。這便利XML數據表現形式更有終端開放性。如果希望盡可能延伸持久化對象圖的使用範圍時,SOAP格式和XML格式是理想選擇。

(3)使用特性對序列化的控制

要讓一個對象支持.Net序列化服務,用戶必須為每一個關聯的類加上[Serializable]特性。如果類中有些成員不適合參與序列化(比如:密碼字段),可以在這些域前加上[NonSerialized]特性。

2.使用二進制序列化和反序列化

(1)二進制序列化與反序列化的程序示例

[Serializable] //必須添加序列化特性

public class Person

{

private string Name;//姓名

private bool Sex;//性別,是否是男

public Person(string name, bool sex)

{

this.Name = name;

this.Sex = sex;

}

public override string ToString()

{

return "姓名:" + this.Name + "\t性別:" + (this.Sex ? "男" : "女");

}

}

[Serializable] //必須添加序列化特性

public class Programmer : Person

{

private string Language;//編程語言

public Programmer(string name, bool sex, string language) : base(name, sex)

{

this.Language = language;

}

public override string ToString()

{

return base.ToString() + "\t編程語言:" + this.Language;

}

}

class Program

{

static void Main(string[] args)

{

//創建Programmer列表,並添加對象

List<Programmer> list = new List<Programmer>();

list.Add(new Programmer("李誌偉", true, "C#"));

list.Add(new Programmer("Coder2", false, "C++"));

list.Add(new Programmer("Coder3", true, "Java"));

//使用二進制序列化對象

string fileName = @"D:\users\lizw\桌面\Programmers.dat";//文件名稱與路徑

Stream fStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite);

BinaryFormatter binFormat = new BinaryFormatter();//創建二進制序列化器

binFormat.Serialize(fStream, list);

//使用二進制反序列化對象

list.Clear();//清空列表

fStream.Position = 0;//重置流位置

list = (List<Programmer>)binFormat.Deserialize(fStream);//反序列化對象

foreach (Programmer p in list)

{

Console.WriteLine(p);

}

Console.Read();

}

}

(2)總結

使用二進制序列化,必須為每一個要序列化的的類和其關聯的類加上[Serializable]特性,對類中不需要序列化的成員可以使用[NonSerialized]特性。

二進制序列化對象時,能序列化類的所有成員(包括私有的),且不需要類有無參數的構造方法。

使用二進制格式序列化時,它不僅是將對象的字段數據進行持久化,也持久化每個類型的完全限定名稱和定義程序集的完整名稱(包括包稱、版本、公鑰標記、區域性),這些數據使得在進行二進制格式反序列化時亦會進行類型檢查。所以反序列化時的運行環境要與序列化時的運行環境要相同,否者可能會無法反序列化成功。

3.使用SOAP方式序列化和反序列化

(1)SOAP序列化與反序列化的程序示例

[Serializable] //必須添加序列化特性

public class Person

{

private string Name;//姓名

private bool Sex;//性別,是否是男

public Person(string name, bool sex)

{

this.Name = name;

this.Sex = sex;

}

public override string ToString()

{

return "姓名:" + this.Name + "\t性別:" + (this.Sex ? "男" : "女");

}

}

[Serializable] //必須添加序列化特性

public class Programmer : Person

{

private string Language;//編程語言

public Programmer(string name, bool sex, string language) : base(name, sex)

{

this.Language = language;

}

public override string ToString()

{

return base.ToString() + "\t編程語言:" + this.Language;

}

}

class Program

{

static void Main(string[] args)

{

//實例化對象

Programmer p = new Programmer("李誌偉", true, "C、C#、C++、Java");

//使用SOAP序列化對象

string fileName = @"D:\users\lizw\桌面\Programmers.xml";//文件名稱與路徑

Stream fStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite);

SoapFormatter soapFormat = new SoapFormatter();//創建SOAP序列化器

soapFormat.Serialize(fStream, p);//SOAP不能序列化泛型對象

//使用SOAP反序列化對象

fStream.Position = 0;//重置流位置

p = null;

p = (Programmer)soapFormat.Deserialize(fStream);

Console.WriteLine(p);

Console.Read();

}

}

(2)總結

SOAP序列化與二進制序列化的區別是:SOAP序列化不能序列化泛型類型。與二進制序列化一樣在序列化時不需要向序列化器指定序列化對象的類型。而XML序列化需要向XML序列化器指定序列化對象的類型。

4.使用XML方式序列化和反序列化

(1)XML序列化與反序列化的程序示例

public class Person

{

public string Name;//姓名

public bool Sex;//性別,是否是男

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

public Person(string name, bool sex)

{

this.Name = name;

this.Sex = sex;

}

public override string ToString()

{

return "姓名:" + this.Name + "\t性別:" + (this.Sex ? "男" : "女");

}

}

public class Programmer : Person

{

public string Language;//編程語言

public Programmer() { }//必須提供無參構造器,否則XmlSerializer將出錯

public Programmer(string name, bool sex, string language) : base(name, sex)

{

this.Language = language;

}

public override string ToString()

{

return base.ToString() + "\t編程語言:" + this.Language;

}

}

class Program

{

static void Main(string[] args)

{

//創建Programmer列表,並添加對象

List<Programmer> list = new List<Programmer>();

list.Add(new Programmer("李誌偉", true, "C#"));

list.Add(new Programmer("Coder2", false, "C++"));

list.Add(new Programmer("Coder3", true, "Java"));

//使用XML序列化對象

string fileName = @"D:\users\lizw\桌面\Programmers.xml";//文件名稱與路徑

Stream fStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite);

XmlSerializer xmlFormat = new XmlSerializer(

typeof(List<Programmer>),

new Type[] { typeof(Programmer),typeof(Person) }

);//創建XML序列化器,需要指定對象的類型

xmlFormat.Serialize(fStream, list);

//使用XML反序列化對象

fStream.Position = 0;//重置流位置

list.Clear();

list = (List<Programmer>)xmlFormat.Deserialize(fStream);

foreach (Programmer p in list)

{

Console.WriteLine(p);

}

Console.Read();

}

}

(2)總結

使用XML序列化或反序列化時,需要對XML序列化器指定需要序列化對象的類型和其關聯的類型。

XML序列化只能序列化對象的公有屬性,並且要求對象有一個無參的構造方法,否者無法反序列化。

[Serializable]和[NonSerialized]特性對XML序列化無效!所以使用XML序列化時不需要對對象增加[Serializable]特性。

5.XML序列化對象詳解

(1)說明

本節主要介紹:使用特性控制對象序列化成XML文件的格式。

(2)使用XmlElement(默認值)

類聲明:

public class Person

{

[XmlElement]

public string Name;//使用[XmlElement]特性

public bool Sex;//默認使用了[XmlElement]特性

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

序列化生成的XML文件:

<Personxmlns:xsi="..."xmlns:xsd="...">

<Name>李誌偉</Name>

<Sex>true</Sex>

</Person>

(3)使用XmlAttribute

類聲明:

public class Person

{

[XmlElement]

public string Name;

[XmlAttribute]

public bool Sex;

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

序列化生成的XML文件:

<Personxmlns:xsi="..."xmlns:xsd="..."Sex="true">

<Name>李誌偉</Name>

</Person>

(4)使用XmlText

類聲明:

public class Person

{

[XmlText]

public string Name;

[XmlAttribute]

public bool Sex;

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

序列化生成的XML文件:

<Personxmlns:xsi="..."xmlns:xsd="..."Sex="true">李誌偉</Person>

(5)使用XmlType和XmlAttribute(重命名節點名稱)

類聲明:

[XmlType("個人信息")]

public class Person

{

[XmlAttribute("姓名")]

public string Name;

[XmlAttribute("性別")]

public bool Sex;

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

序列化生成的XML文件:

<個人信息xmlns:xsi="..."xmlns:xsd="..."姓名="李誌偉"性別="true" />

(6)列表和數組的序列化

類聲明:

[XmlType("個人信息")]

public class Person

{

[XmlAttribute("姓名")]

public string Name;

[XmlAttribute("性別")]

public bool Sex;

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

class Program

{

static void Main(string[] args)

{

Person p = new Person();

p.Name = "李誌偉";

p.Sex = true;

Person[] ps = new Person[3];

ps[0] = p;

ps[1] = p;

ps[2] = p;

//使用XML序列化對象

string fileName = @"D:\users\lizw\桌面\Programmers.xml";//文件名稱與路徑

Stream fStream = new FileStream(fileName, FileMode.Create);

XmlSerializer xmlFormat = new XmlSerializer(typeof(Person[]));

xmlFormat.Serialize(fStream, ps);//序列化對象

fStream.Dispose();//關閉文件

Console.WriteLine("OK!");

Console.Read();

}

}

序列化生成的XML文件:

<ArrayOf個人信息xmlns:xsi="..."xmlns:xsd="...">

<個人信息姓名="李誌偉"性別="true" />

<個人信息姓名="李誌偉"性別="true" />

<個人信息姓名="李誌偉"性別="true" />

</ArrayOf個人信息>

註意:發現此時的XML文件的根節點名稱變了。此時要重命名根節點應使用如下方式:

[XmlType("個人信息")]

public class Person

{

[XmlAttribute("姓名")]

public string Name;

[XmlAttribute("性別")]

public bool Sex;

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

[XmlType("人員信息")]

public class PersonArray : List<Person> { }

class Program

{

static void Main(string[] args)

{

Person p = new Person();

p.Name = "李誌偉";

p.Sex = true;

PersonArray ps = new PersonArray();

ps.Add(p);

ps.Add(p);

ps.Add(p);

//使用XML序列化對象

string fileName = @"D:\users\lizw\桌面\Programmers.xml";//文件名稱與路徑

Stream fStream = new FileStream(fileName, FileMode.Create);

XmlSerializer xmlFormat = new XmlSerializer(typeof(PersonArray));

xmlFormat.Serialize(fStream, ps);//序列化對象

fStream.Dispose();//關閉文件

Console.WriteLine("OK!");

Console.Read();

}

}

序列化生成的XML文件:

<人員信息xmlns:xsi="..."xmlns:xsd="...">

<個人信息姓名="李誌偉"性別="true" />

<個人信息姓名="李誌偉"性別="true" />

<個人信息姓名="李誌偉"性別="true" />

</人員信息>

(7)列表和數組的做為數據成員的序列化

類聲明:

[XmlType("信息")]

public class Person

{

[XmlAttribute("姓名")]

public string Name;

[XmlAttribute("性別")]

public bool Sex;

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

public class PersonArray

{

public List<Person> Array=new List<Person>();

public Person Person = new Person();

}

class Program

{

static void Main(string[] args)

{

PersonArray ps = new PersonArray();

ps.Person = new Person();

ps.Person.Name = "李誌偉";

ps.Person.Sex = true;

ps.Array.Add(ps.Person);

ps.Array.Add(ps.Person);

ps.Array.Add(ps.Person);

//使用XML序列化對象

string fileName = @"D:\users\lizw\桌面\Programmers.xml";//文件名稱與路徑

Stream fStream = new FileStream(fileName, FileMode.Create);

XmlSerializer xmlFormat = new XmlSerializer(typeof(PersonArray));

xmlFormat.Serialize(fStream, ps);//序列化對象

fStream.Dispose();//關閉文件

Console.WriteLine("OK!");

Console.Read();

}

}

序列化生成的XML文件:

<PersonArrayxmlns:xsi="..."xmlns:xsd="...">

<Array>

<個人信息姓名="李誌偉"性別="true" />

<個人信息姓名="李誌偉"性別="true" />

<個人信息姓名="李誌偉"性別="true" />

</Array>

<Person姓名="李誌偉"性別="true" />

</PersonArray>

註意:假設這裏需要為Array和Person的節點重命名,代碼如下:

[XmlType("信息")]

public class Person

{

[XmlAttribute("姓名")]

public string Name;

[XmlAttribute("性別")]

public bool Sex;

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

public class PersonArray

{

[XmlArrayItem("個人信息")]

[XmlArray("人員信息")]

public List<Person> Array=new List<Person>();

public Person Person = new Person();

}

序列化生成的XML文件:

<PersonArrayxmlns:xsi="..."xmlns:xsd="...">

<人員信息>

<個人信息姓名="李誌偉"性別="true" />

<個人信息姓名="李誌偉"性別="true" />

<個人信息姓名="李誌偉"性別="true" />

</人員信息>

<Person姓名="李誌偉"性別="true" />

</PersonArray>

註意:把“人員信息”節點去掉呢(直接出現“個人信息”節點)

[XmlType("信息")]

public class Person

{

[XmlAttribute("姓名")]

public string Name;

[XmlAttribute("性別")]

public bool Sex;

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

public class PersonArray

{

[XmlElement("個人信息")]

public List<Person> Array=new List<Person>();

public Person Person = new Person();

}

序列化生成的XML文件:

<PersonArrayxmlns:xsi="..."xmlns:xsd="...">

<個人信息姓名="李誌偉"性別="true" />

<個人信息姓名="李誌偉"性別="true" />

<個人信息姓名="李誌偉"性別="true" />

<Person姓名="李誌偉"性別="true" />

</PersonArray>

(8)類型繼承與反序列化

類聲明:

public class Base { }

[XmlType("信息A")]

public class PersonA : Base

{

[XmlAttribute("姓名")]

public string Name;

[XmlAttribute("性別")]

public bool Sex;

public PersonA() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

[XmlType("信息B")]

public class PersonB : Base

{

[XmlElement("姓名")]

public string Name;

[XmlElement("年齡")]

public int Age;

public PersonB() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

[XmlType("人員信息")]

public class PersonArray

{

[XmlArrayItem(typeof(PersonA)), XmlArrayItem(typeof(PersonB))]

public List<Base> ListPerson=new List<Base>();

}

class Program

{

static void Main(string[] args)

{

PersonA pa = new PersonA();

pa.Name = "李誌偉A";

pa.Sex = true;

PersonB pb = new PersonB();

pb.Name = "李誌偉B";

pb.Age = 21;

PersonArray ps = new PersonArray();

ps.ListPerson.Add(pa);

ps.ListPerson.Add(pa);

ps.ListPerson.Add(pb);

ps.ListPerson.Add(pb);

//使用XML序列化對象

string fileName = @"D:\users\lizw\桌面\Programmers.xml";//文件名稱與路徑

Stream fStream = new FileStream(fileName, FileMode.Create);

XmlSerializer xmlFormat = new XmlSerializer(typeof(PersonArray));

xmlFormat.Serialize(fStream, ps);//序列化對象

fStream.Dispose();//關閉文件

Console.WriteLine("OK!");

Console.Read();

}

}

序列化生成的XML文件:

<人員信息xmlns:xsi="..."xmlns:xsd="...">

<ListPerson>

<信息A姓名="李誌偉A"性別="true" />

<信息A姓名="李誌偉A"性別="true" />

<信息B>

<姓名>李誌偉B</姓名>

<年齡>21</年齡>

</信息B>

<信息B>

<姓名>李誌偉B</姓名>

<年齡>21</年齡>

</信息B>

</ListPerson>

</人員信息>

註意:同時為列表成員指定多個[XmlArrayItem(typeof(XXX))]可實現多種派生類型混在一起輸出。

(9)排除不需要序列化的成員

類聲明:

public class Person

{

public string Name;

[XmlIgnore]// 這個屬性將不會參與序列化

public bool Sex;

public Person() { }

}

序列化生成的XML文件:

<Personxmlns:xsi="..."xmlns:xsd="...">

<Name>李誌偉</Name>

</Person>

(10)強制指定成員的序列化順序

類聲明:

public class Person

{

[XmlElement(Order = 2)]

public string Name;

[XmlElement(Order = 1)]

public bool Sex;

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

}

序列化生成的XML文件:

<Personxmlns:xsi="..."xmlns:xsd="...">

<Sex>true</Sex>

<Name>李誌偉</Name>

</Person>

(11)自定義序列化行為

類聲明:

public class Person : IXmlSerializable

{

public string Name;

public bool Sex;

public Person() { }//必須提供無參構造器,否則XmlSerializer將出錯

public System.Xml.Schema.XmlSchema GetSchema()

{

return null;

}

public void ReadXml(System.Xml.XmlReader reader)

{

Name = reader.GetAttribute("姓名");

Sex = reader.GetAttribute("性別").Equals("男") ? true : false;

}

public void WriteXml(System.Xml.XmlWriter writer)

{

writer.WriteAttributeString("姓名", Name);

writer.WriteAttributeString("性別", Sex ? "男" : "女");

}

}

序列化生成的XML文件:

<Person姓名="李誌偉"性別="男" />

(12)序列化設置XML命名空間

類聲明:

[XmlRoot(Namespace = "http://msdn.microsoft.com/vsdh.xsd")]

public class Person

{

public string Name;

public bool Sex;

public Person() { }

}

序列化生成的XML文件:

<Personxmlns:xsi="..."xmlns:xsd="..."xmlns="http://msdn.microsoft.com/vsdh.xsd">

<Name>李誌偉A</Name>

<Sex>true</Sex>

</Person>

(13)XML的使用建議

在服務端,C#代碼中:

1. 建議不用使用低級別的XML API來使用XML,除非你是在設計框架或者通用類庫。

2. 建議使用序列化、反序列化的方法來生成或者讀取XML

3. 當需要考慮使用XML時,先不要想著XML結構,先應該定義好數據類型。

4. 列表節點不要使用[XmlElement],它會讓所有子節點【升級】,顯得結構混亂。

5. 如果希望序列化的XML長度小一點,可以采用[XmlAttribute],或者指定一個更短小的別名。

6. 不要在一個列表中輸出不同的數據類型,這樣的XML結構的可讀性不好。

7. 盡量使用UTF-8編碼,不要使用GB2312編碼。

在客戶端,JavaScript代碼中,我不建議使用XML,而是建議使用JSON來代替XML,因為:

1. XML文本的長度比JSON要長,會占用更多的網絡傳輸時間(畢竟數據保存在服務端,所以傳輸是免不了的)。

2. 在JavaScritp中使用XML比較麻煩(還有瀏覽器的兼容問題),反而各種瀏覽器對JSON有非常好的支持。

(14)反序列化的使用總結

如果XML是由類型序列化得到那的,那麽反序列化的調用代碼是很簡單的,反之,如果要面對一個沒有類型的XML,就需要我們先設計一個(或者一些)類型出來,這是一個逆向推導的過程,請參考以下步驟:

1. 首先要分析整個XML結構,定義與之匹配的類型,

2. 如果XML結構有嵌套層次,則需要定義多個類型與之匹配,

3. 定義具體類型(一個層級下的XML結構)時,請參考以下表格。

XML形式

處理方法

補充說明

XmlElement

定義一個屬性

屬性名與節點名字匹配

XmlAttribute

[XmlAttribute] 加到屬性上

InnerText

[InnerText] 加到屬性上

一個類型只能使用一次

節點重命名

根節點:[XmlType("testClass")]
元素節點:[XmlElement("name")]
屬性節點:[XmlAttribute("id")]
列表子元素節點:[XmlArrayItem("Detail")]
列表元素自身:[XmlArray("Items")]

6.自定義序列化(僅適用於二進制與SOAP)

(1)自定義序列化的實現方式

可以通過在對象上實現 ISerializable 接口來自定義序列化過程。這一功能在反序列化後成員變量的值失效時尤其有用,但是需要為變量提供值以重建對象的完整狀態。要實現 ISerializable,需要實現GetObjectData()方法以及一個特殊的構造函數,在反序列化對象時要用到此構造函數。

(2)示例程序

[Serializable]

public class Person : ISerializable

{

public string Name;

public bool Sex;

public Person() { }

//必須的夠著方法,反序列化時調用

protected Person(SerializationInfo info, StreamingContext context)

{

Name = info.GetString("姓名");

Sex = info.GetBoolean("性別");

}

//序列化時調用

public void GetObjectData(SerializationInfo info, StreamingContext context)

{

info.AddValue("姓名", Name + "(自定義序列化)");

info.AddValue("性別", Sex);

}

public override string ToString()

{

return "姓名:" + this.Name + "\t性別:" + (this.Sex ? "男" : "女");

}

}

class Program

{

static void Main(string[] args)

{

Person p = new Person();

p.Name = "李誌偉A";

p.Sex = true;

//使用二進制序列化對象

string fileName = @"D:\users\lizw\桌面\Programmers.xml";//文件名稱與路徑

Stream fStream = new FileStream(fileName, FileMode.Create);

BinaryFormatter binFormat = new BinaryFormatter();//創建二進制序列化器

binFormat.Serialize(fStream, p);//序列化對象

//使用二進制反序列化對象

fStream.Position = 0;//重置流位置

p = (Person)binFormat.Deserialize(fStream);//反序列化對象

Console.WriteLine(p);

fStream.Dispose();//關閉文件

Console.WriteLine("OK!");

Console.Read();

}

}

註意:在序列化過程中調用 GetObjectData()時,需要填充方法調用中提供的 SerializationInfo對象。只需按名稱/值對的形式添加將要序列化的變量。其名稱可以是任何文本。只要已序列化的數據足以在反序列化過程中還原對象,便可以自由選擇添加至 SerializationInfo 的成員變量。如果基對象實現了 ISerializable,則派生類應調用其基對象的 GetObjectData()方法。同樣,在反序列化時也會調用含有(SerializationInfo info,StreamingContext context)參數的特殊的夠著方法!否者將無法反序列化!!!

C#對象序列化與反序列化