定义:
序列化就是将我们程序中的对象实例转换成流存储在存储介质上或在网络中传输。
反序列化就是把已存入的介质中或接收的网络流中的内容转换成程序运行中的对象。
目的:
1 .以某种存储形式使自定义对象持久化
2.将对象从一个地方传递到另一个地方。
在.NET中提供了两种序列化的方式:
一.BinaryFormatter
是使用二进制来格式化,它甚至可以把私有成员都序列化。
要使某个类的对象可以被序列化,必须要在类的前面加上 [Serializable]特性,否则会产生异常。如果要对子类进行序列化,那必须要保证其父类也具有[Serializable]特性。如果有类中有一个成员变量是个对象,那也要保证该成员对象的类具有[Serializable]特性,否则也会抛出异常。
例如有下面2个类:
[Serializable]
public class People
{
protected int age;
protected string name;
public int Age
{
get { return age; }
set { age = value; }
}
{
get { return name; }
set { name = value; }
}
}
[Serializable]
public class Student : People
{
private string school;
public string School
{
get { return school; }
set { school = value; }
}
public void OutputInfo()
{
Console.WriteLine("age: {0},name: {1},school: {2}",age,name,school);
}
}
先初始化一个实例:
Student stu = new Student();
stu.Age = 18;
stu.Name = "bear";
stu.School = "ysNo1";
对它进行二进制序列化和反序列化:
// Serialize
Stream stream = new FileStream(@"D:\student.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite);
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, stu);
stream.Close();
// Deserialize
FileStream fs = new FileStream(@"D:\student.bin", FileMode.OpenOrCreate);
IFormatter bf = new BinaryFormatter();
Student s = (Student)bf.Deserialize(fs);
fs.Close();
s.OutputInfo();
二. SoapFormatter
是Xml序列化。XML序列化只可以把公有成员变量序列化到流中去,私有成员变量无法被序列化,但如果私有成员变量有对应的公有属性的话,那私有成员变量照样可以被序列化。
对于上例,只需要把BinaryFormatter换成SoapFormatter就可以了。
// Serialize
Stream stream = new FileStream(@"D:\student.sd", FileMode.OpenOrCreate, FileAccess.ReadWrite);
IFormatter formatter = new SoapFormatter();
formatter.Serialize(stream, stu);
stream.Close();
// Deserialize
FileStream fs = new FileStream(@"D:\student.sd", FileMode.OpenOrCreate);
IFormatter sf = new SoapFormatter();
Student s = (Student)sf.Deserialize(fs);
fs.Close();
s.OutputInfo();
这里保存文件的扩展名是任意的。
使用XML序列化的文件student.sd如下:
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<a1:Form1_x002B_Student id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/WindowsApplication13/WindowsApplication13%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<school id="ref-3">ysNo1</school>
<age>18</age>
<name id="ref-4">bear</name>
<People_x002B_age>18</People_x002B_age>
<People_x002B_name href="#ref-4"/>
</a1:Form1_x002B_Student>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
这样序列化的结果有很多Soap的额外信息,如果我们不需要这些信息,就可以使用另一个序列化类XmlSerializer。
三. XmlSerializer
1、XmlSerializer类序列化对象的时候,[Serializable]和[NonSerializable]属性是不起作用的,可以使用XmlIgnore属性,替代NonSerializable属性。
2、XmlSerializer类序列化对象的时候,不能安全地访问私有变成员,所以要将私有成员改为公共成员,或者提供合适的公共属性。
下面是使用XmlSerializer序列化和反序列化:
// Serialize
FileStream fs = new FileStream(@"D:\student.xml", FileMode.Create);
XmlSerializer xs = new XmlSerializer(typeof(Student));
xs.Serialize(fs, stu);
fs.Close();
// Deserialize
FileStream dfs = new FileStream(@"D:\student.xml", FileMode.Open);
XmlSerializer dxs = new XmlSerializer(typeof(Student));
Student s = dxs.Deserialize(dfs) as Student;
dfs.Close();
s.OutputInfo();
这样student.xml就很简单了:
<?xml version="1.0"?>
<Student xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Age>18</Age>
<Name>bear</Name>
<School>ysNo1</School>
</Student>
对于一个用Serializable标识的类,在用BinaryFormatter或SoapFormatter序列化时,可以通过使用 [NonSerialized] 属性标记成员变量来防止它们被序列化,如下所示:
[Serializable]
public class MyObject
{
public int n1;
[NonSerialized]
public int n2; //此属性不会被序列化,无论二进制序列化还是XML序列化,但XmlSerializer类会忽略此设置,可以将其序列化。
public String str;
}
四. 自定义序列化
如果对现有的序列化方式不完全满意,可以通过在对象上实现 ISerializable 接口来自定义序列化过程。在实现ISerializable接口实现自定义序列化的时候,必须为类添加[Serializable]属性。
ISerializable接口的声明如下
public interface ISerializable
{
void GetObjectData (SerializationInfo info, StreamingContext context);
}
其中有一个需要实现的方法GetObjectData(),此方法是在序列化的时候被自动调用的,它的作用是在序列化对象时候取得要被序列化的成员变量的信息,你可以在这里控制哪些成员变量需要被序列化。
另外为了实现对象的反序列化,你除了要实现ISerializable接口,还要为该类添加一个特殊的构造函数,构造函数的声明应当如下:
public ×××(SerializationInfo info, StreamingContext context)
{
}
该构造函数在反序列化的时候被自动调用,其作用是从序列化的信息当中取出成员变量的值,以便初始化成员变量,来构造对象。
GetObjectData()方法和特殊构造函数有两个形参,其中第一个形参SerializationInfo最重要,它的是用来保存序列化或反序列化中用到的所有的成员变量的数据。
SerializationInfo类中最常用的有两个(类)方法:
void AddValue(string,object):是把要被序列化的成员变量加入到SerializationInfo对象中去,并指定一个名称,以便序列化。
××× Get×××(string):是根据名称从序列化流中把相应的内容取出来,然后就可以把取出来的值赋给相应的成员变量了。
注意的是:自定义序列化只能使用BinaryFormatter和SoapFormatter,而不能用XmlSerializer。
将Student改写如下:
[Serializable]
public class Student : People, ISerializable
{
public Student()
: base()
{ }
private string school;
public string School
{
get { return school; }
set { school = value; }
}
public void OutputInfo()
{
Console.WriteLine("age: {0},name: {1},school: {2}",age,name,school);
}
public Student(SerializationInfo info, StreamingContext context)
{
base.age = (int)(info.GetValue("age", typeof(int)));
school = (string)(info.GetValue("school", typeof(string)));
}
#region ISerializable Members
[SecurityPermissionAttribute(SecurityAction.LinkDemand,
Flags = SecurityPermissionFlag.SerializationFormatter)]
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("age", base.Age);
info.AddValue("school", school);
Console.WriteLine("get object data is called");
}
#endregion
}
get object data is called
age: 18,name: ,school: ysNo1
// Create the Button.
Button origianlButton = new Button();
origianlButton.Height = 50;
origianlButton.Width = 100;
origianlButton.Background = Brushes.AliceBlue;
origianlButton.Content = "Click Me";
// Save the Button to a string.
string savedButton = XamlWriter.Save(origianlButton);
// Load the button
StringReader stringReader = new StringReader(savedButton);
XmlReader xmlReader = XmlReader.Create(stringReader);
Button readerLoadButton = (Button)XamlReader.Load(xmlReader);