C# 中的序列化和反序列化【转】
序列化就是将我们程序中的对象通过字节流写入存储媒体或网络流中。 反序列化就是把已存入的媒体或接收的网络流中的内容转换成程序运行中的对象。 这两个过程结合起来,可以轻松地存储和传输数据。
使用序列化场景: 1、在用户登录后,对界面作一些个性化设置(如:背景色、布局、字体等),为了使用户关闭网页后能够保留设置,以便在下次登录时再加载上次的设置。我们可以将用户的设置信息保存在一个对象中,然后把该对象序列化保存在表的某个字段中,在加载网页的时候取出字段中的信息,并反序列化生成设置对象,应用到用户界面上。 2、对用户的一些不用作查询的信息(如:住址、Email、家庭成员、工作经历等)序列化后保存在一个表的字段中,在需求发生变化时(如增加用户新的信息),不用动态增加字段。在需要使用的时候,取出字段中的的信息反序列化成对象就可以了。 3、在点对点两人聊天系统中,一个用户输入的内容(如:彩色文字、图片等)后显示输入时的内容和样式,另一个用户界面中也应当显示同样的内容和样式。这时我们可以把用户输入的内容(如:彩色文字、图片等)封装为一个对象,然后序列化到二进制网络流中去,在另一端,取出二进制流并反序列化成对象,然后显示在界面上。
.NET Framework 提供两种序列化技术: 二进制序列化:System.Runtime.Serialization.Formatters.Binary; XML序列化:System.Runtime.Serialization.Formatters.Soap;须要加载(引用)对应的DLL文件
二进制序列化的保真度非常强,可以把私有成员变量序列化到流中去。 XML序列化只可以把公有成员变量序列化到流中去,私有成员变量无法被序列化,但如果私有成员变量有对应的公有属性的话,那私有成员变量照样可以被序列化。
要使某个类的对象可以被序列化,必须要在类的前面加上 [Serializable]属性,否则会产生异常。如果要对子类进行序列化,那必须要保证其父类也具有[Serializable]属性。如果有类中有一个成员变量是个对象,那也要保证该成员对象的类具有[Serializable]属性,否则也会抛出异常。
例:有两个类 using System;
namespace SerializableDemo
{
[Serializable]
public class Person
{
protected string name;
protected int age;
public string Name
{
get { return name;}
set { name = value;}
}
public int Age
{
get{ return age;}
set{ age = value;}
}
public Person()
{
}
public virtual void Speak()
{
Console.WriteLine("姓名:{0},年龄:{1}",name,age);
}
}
[Serializable]
public class Student : Person
{
private string school;
public string School
{
get{ return school;}
set{ school = value;}
}
public override void Speak()
{
Console.WriteLine("姓名:{0},年龄:{1},学校:{2}",name,age,school);
}
}
}
二进制序列化 二进制序列化是通过BinaryFormatter对象实现的,BinaryFormatter对象有两个方法: void Serialize(Stream s,Object o) 将对象o序列化到流s中去。 object Deserialize(Stream s) 从流s中读取数据反序列化成对象。 对上面Student类的对象进行二进制序列化: Student stu= new Student();
stu.Name="svse";
stu.Age=20;
stu.School="厚溥";
//二进制序列化
System.IO.FileStream fs= new FileStream("d:\\svse.dat",FileMode.Create);
BinaryFormatter bf=new BinaryFormatter();
bf.Serialize(fs,stu);
fs.Close();
Console.WriteLine("OK");
反序列化代码 FileStream fs = new FileStream("d:\\svse.dat", FileMode.OpenOrCreate);
BinaryFormatter bf = new BinaryFormatter();
Student s = (Student)bf.Deserialize(fs);
fs.Close();
s.Speak();
XML序列化 XML序列化是通过对象实现的,它是将对象序列化成XML格式。
SoapFormatter 类
也有两个方法,同BinaryFormatter。对上面Student类的对象进行XML序列化: Student stu= new Student();
stu.Name="svse";
stu.Age=20;
stu.School="厚溥";
//XML序列化
System.IO.FileStream fs= new FileStream("d:\\svse.dat",FileMode.Create);
SoapFormatter sf=new SoapFormatter ();
sf.Serialize(fs,stu);
fs.Close();
Console.WriteLine("OK");
反序列化代码 FileStream fs = new FileStream("d:\\svse.dat", FileMode.OpenOrCreate);
SoapFormatter sf = new SoapFormatter();
Student s = (Student)sf.Deserialize(fs);
fs.Close();
s.Speak();
从图中我们可以看出用SoapFormatter序列化出的XML文件有好多Soap的额外信息,要想除去这些额外的Soap信息,那我们可以使用XmlSerializer类对对象进行序列化。
XmlSerializer类
命名空间:System.Xml.Serialization; 1、XmlSerializer类序列化对象的时候,[Serializable]和[NonSerializable]属性是不起作用的,可以使用XmlIgnore属性,替代NonSerializable属性。 2、XmlSerializer类序列化对象的时候,不能安全地访问私有成员,所以要将私有成员改为公共成员,或者提供合适的公共属性。 使用XmlSerializer类对上面Student类的对象进行XML序列化: Student stu= new Student();
stu.Name="svse";
stu.Age=20;
stu.School="厚溥";
FileStream fs = new FileStream("d:\\svse.dat", FileMode.Create);
XmlSerializer xs = new XmlSerializer(typeof(Student));
xs.Serialize(fs, s);
fs.Close();
Console.WriteLine("OK");
使用XmlSerializer类反序列化
FileStream fs = new FileStream("d:\\svse.dat",FileMode.OpenOrCreate);
XmlSerializer xml = new XmlSerializer(typeof(Student));
Student s = (Student)(xml.Deserialize(fs));
fs.Close();
s.speak();
选择性序列化 类通常包含不应被序列化的字段。可以通过使用 [NonSerialized] 属性标记成员变量来防止它们被序列化,如下所示: [Serializable] public class MyObject { public int n1; [NonSerialized] public int n2;
public String str; }
自定义序列化 如果你对现有的序列化方式不完全满意,可以通过在对象上实现 ISerializable(命名空间:System.Runtime.Serialization) 接口来自定义序列化过程。你在实现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):是根据名称从序列化流中把相应的内容取出来,然后就可以把取出来的值赋给相应的成员变量了。
自定义序列化的水果类 [Serializable] class Fruit:ISerializable { private string ids; private string name; private decimal price; public Fruit(string ids, string name, decimal price) { this.ids = ids; this.name = name; this.price = price; } public string Ids { get{ return ids;} } public string Name { get{ return name;} } public decimal Price { get{ return price;} } public void Show() { Console.WriteLine(ids+name+price); }
//反序列化时调用 public Fruit(SerializationInfo info, StreamingContext context) { ids = info.GetString("vids"); name = info.GetString("vname"); price = info.GetDecimal("vprice"); }
//序列化时自动调用 public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("vids", ids + "是编号"); info.AddValue("vname", name + "是名称"); info.AddValue("vprice", price); } } 将其进行序列化(二进制序列化) FileStream fs = new FileStream("d:\\test.dat", FileMode.OpenOrCreate); Fruit f = new Fruit("k001", "苹果", (decimal)1.2); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fs, f); fs.Close(); Console.WriteLine("OK"); 反序列化 FileStream fs = new FileStream("d:\\test.dat", FileMode.OpenOrCreate); BinaryFormatter bf = new BinaryFormatter(); Fruit f = (Fruit)bf.Deserialize(fs); fs.Close(); f.Show();