.net对象序列化解析

  一.二进制格式器(Binary Formatter) vs XML格式器(XML Formatter):

  下面我先向你们介绍两种不一样的格式器,分别用它们如何实现序列化机制和反序列化机制,请看下面的代码:程序员

#region Binary Serializers
public static System.IO.MemoryStream SerializeBinary(object request) {
 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer = 
  new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
 System.IO.MemoryStream memStream = new System.IO.MemoryStream();
 serializer.Serialize(memStream, request);
 return memStream;
}网络

public static object DeSerializeBinary(System.IO.MemoryStream memStream) {
 memStream.Position=0;
 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer = 
  new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
 object newobj = deserializer.Deserialize(memStream);
 memStream.Close();
 return newobj;
}
#endregion数据结构

#region XML Serializers
public static System.IO.MemoryStream SerializeSOAP(object request) {
 System.Runtime.Serialization.Formatters.Soap.SoapFormatter serializer = 
  new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
 System.IO.MemoryStream memStream = new System.IO.MemoryStream();
 serializer.Serialize(memStream, request);
 return memStream;
}并发

public static object DeSerializeSOAP(System.IO.MemoryStream memStream) {
 object sr;
 System.Runtime.Serialization.Formatters.Soap.SoapFormatter deserializer = 
  new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
 memStream.Position=0;
 sr = deserializer.Deserialize(memStream);
 memStream.Close();
 return sr;
}
#endregion框架

  从上面的代码咱们能够发现不管运用哪一种格式器,其基本的过程都是同样的,并且都是很是容易实现的,惟一的不一样就是定义格式器的类型不一样。不过在实际的应用中,二进制格式器每每应用于通常的桌面程序和网络通信程序中,而XML格式器禀承了XML技术的优势,大多数被应用于.Net Remoting和XML Web服务等领域。下面咱们来分析一下两种格式器各自的优势。

  二进制序列化的优势:

  1. 全部的类成员(包括只读的)均可以被序列化;

  2. 性能很是好。

  XML序列化的优势:

  1. 互操做性好;

  2. 不须要严格的二进制依赖;

  3. 可读性强。

  经过分析上面的代码,咱们知道了选择二进制序列化的方式仍是选择XML序列化的方式仅仅是对不一样的格式器进行选择而已。你能够根据实际的须要选择相应的格式器完成序列化和反序列化工做。同时请注意,代码中的序列化函数和反序列化函数仅仅是在调用Serialize()和Deserialize()这两个核心函数上产生了差异,即它们的参数不一样。所以以上的代码完成了一些最最基本可是很重要的功能,你能够将它们运用在你的程序中,或是将其进行适当扩充以知足程序的特定须要。函数

  二.序列化机制对类的要求:

  若是你要对一个对象进行序列化,那么你必须将它的类型标记为[Serializable()],该操做是经过SerializableAttribute属性来实现的。将SerializableAttribute属性应用于一种数据类型可代表该数据类型的实例能够被序列化。若是正在序列化的对象图中的任何类型未应用SerializableAttribute属性,公共语言运行库则会引起SerializationException。默认状况下,类型中由SerializableAttribute标记的全部公共和私有字段都会进行序列化,除非该类型实现ISerializable接口来重写序列化进程(经过实现该接口咱们即可以实现将在后面介绍的"自定义序列化")。默认的序列化进程会排除用NonSerializedAttribute属性标记的字段,即你能够将该类型标记为[NonSerialized()]以代表它是不能够被序列化的。若是可序列化类型的字段包含指针、句柄或其余某些针对于特定环境的数据结构,而且不能在不一样的环境中以有意义的方式重建,则最好将NonSerializedAttribute属性应用于该字段。有关序列化的更多信息,请参阅System.Runtime.Serialization名字空间中的相关内容。

  下面我给你们介绍一个例子,以显示如何正确的运用SerializableAttribute属性和NonSerializedAttribute属性。该程序中运用到了XML格式器,不过同时给出了二进制格式器为参考(程序中将其用"//"标注),其实现的结果是同样的。该程序实现的功能是在序列化和反序列化操做先后测试对象因包含了[NonSerialized()]的字段而显示不一样的屏幕打印结果。其代码以下:性能

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;
//using System.Runtime.Serialization.Formatters.Binary;测试

public class Test {
 public static void Main() {
  // 建立一个新的测试对象
  TestSimpleObject obj = new TestSimpleObject();this

  Console.WriteLine("Before serialization the object contains: ");
  obj.Print();spa

  // 建立一个文件"data.xml"并将对象序列化后存储在其中
  Stream stream = File.Open("data.xml", FileMode.Create);
  SoapFormatter formatter = new SoapFormatter();
  //BinaryFormatter formatter = new BinaryFormatter();

  formatter.Serialize(stream, obj);
  stream.Close();
  
  // 将对象置空
  obj = null;

  // 打开文件"data.xml"并进行反序列化获得对象
  stream = File.Open("data.xml", FileMode.Open);
  formatter = new SoapFormatter();
  //formatter = new BinaryFormatter();

  obj = (TestSimpleObject)formatter.Deserialize(stream);
  stream.Close();

  Console.WriteLine("");
  Console.WriteLine("After deserialization the object contains: ");
  obj.Print();
 }
}

// 一个要被序列化的测试对象的类
[Serializable()] 
 public class TestSimpleObject {
 public int member1;
 public string member2;
 public string member3;
 public double member4;

 // 标记该字段为不可被序列化的
[NonSerialized()] public string member5; 

public TestSimpleObject() {
 member1 = 11;
 member2 = "hello";
 member3 = "hello";
 member4 = 3.14159265;
 member5 = "hello world!";
}

public void Print() {
 Console.WriteLine("member1 = '{0}'", member1);
 Console.WriteLine("member2 = '{0}'", member2);
 Console.WriteLine("member3 = '{0}'", member3);
 Console.WriteLine("member4 = '{0}'", member4);
 Console.WriteLine("member5 = '{0}'", member5);
}
}

  三.基本序列化(Basic Serialization) vs 自定义序列化(Custom Serialization):

  .Net框架为咱们提供了两种方式的序列化:一种为基本序列化、另外一种为自定义序列化。值得注意的是,序列化的方式和前面提到的序列化的格式是不一样的概念。序列化的方式是指.Net框架将程序的数据转化为能被存储并传输的格式的实际过程,它是无论程序员运用了何种类型的格式器的(二进制格式器仍是XML格式器)。而序列化的格式则指程序的数据是被转化成二进制格式了仍是被转化成XML格式了。

  完成序列化的最简单的方法即是让.Net框架自动为咱们完成整个过程,而咱们没必要去管它内部是如何具体实现的,这种方法即是前面提到的"基本序列化"。在这种方式下,咱们须要作的仅仅是将类标记上[Serializable()]属性。而后.Net框架便调用该类的对象并将它转化为所需的格式。同时你还能够控制其中的某些字段不被序列化,方法就是前面所述的将该字段标记上[NonSerialized()]属性。这样,最最简单和基本的序列化工做就完成了,不过其内部是如何实现的你是不得而知的,同时你也不能进一步控制序列化过程的程序行为。

  若是你要得到对序列化的更大的控制权,那么你就得使用"自定义序列化"的方式。经过使用这种方式,你能够彻底的控制类的哪些部分能被序列化而哪些部分不能,同时你还能够控制如何具体的进行序列化。运用该方式的好处就是能克服基本序列化所会遇到的问题。咱们在运用基本序列化将一个类的对象序列化完毕并存储在文件中后,假设该对象原来有三个字段,若是此时该对象增长了一个字段,那么再将该对象从文件中反序列化出来时会发生字段数不一致的错误。这样的问题是基本序列化所不能解决的,只能运用自定义序列化的方式来解决。

  在介绍自定义序列化以前,我先给出介绍过程当中所要用到的实例程序的代码。这是一个时间安排程序,其中要用到将不一样的时间格式进行转化的操做。因此运用序列化的机制能很好的解决这个问题。

using System;
using System.Runtime.Serialization;

namespace SerializationSample {
[Serializable()]
 public class Schedule {
  protected System.DateTime start;
  protected System.DateTime end;
  // 每一个时间间隔所要增长的毫秒数
  protected long interval;

  public System.DateTime Start {get{return start;}set{start=value;}}
  public System.DateTime End {get{return end;}set{end=value;}}
  public long Interval {get{return interval;}set{interval=value;}}
  public Schedule(System.DateTime Start, System.DateTime End, long Interval) {
   start=Start;
   end=End;
  interval=Interval;
}

// 若是已经到告终束的时间,则返回结束时间,不然返回下一次运行的时间
public System.DateTime NextRunTime {
 get {
  System.TimeSpan ts = new System.TimeSpan(end.Ticks-System.DateTime.Now.Ticks);
  if(ts.Milliseconds>0) {
   return System.DateTime.Now.AddMilliseconds(interval);
  } else {
   return end;
  }
 }
}
}
}

  自定义序列化:

  下面我就向你们介绍自定义序列化以及反序列化的具体过程。首先,程序的类必须实现System.Runtime.Serialization.ISerializable接口,该接口的功能就是容许对象控制其本身的序列化和反序列化过程。因此咱们得从新定义上面的类:

[Serializable()]
public class ScheduleCustom : System.Runtime.Serialization.Iserializable

  接下来,咱们必须对该接口调用GetObjectData()的实现,也即咱们必须在上面的类中给出GetObjectData()的具体实现。其函数原型以下:

void GetObjectData(SerializationInfo info, StreamingContext context);

  上面的类中GetObjectData()的具体实现以下:

public void GetObjectData(SerializationInfo info,StreamingContext context) {
// 运用info对象来添加你所须要序列化的项
info.AddValue("start", start);
info.AddValue("end", end);
info.AddValue("interval", interval);
}

   然而对于这么一个简单的方法,读者可能不能理会到系列化带来的强大功能,因此下面我就给这个方法添加一些东西。若是在系列化过程当中咱们要查看类型为DateTime的"start"属性的输出的话,其结果会是.Net框架默认的格式:

  2002-12-19T14:09:13.3457440-07:00

  而对于没有.Net框架的用户,或是在其余时间区域内的用户而言,这么一个格式的时间多是很是难以理解的,因此咱们有必要将时间的格式转化为格林威治标准时间格式,因而修改GetObjectData()方法以下:

public void GetObjectData(SerializationInfo info,StreamingContext context) {
// 运用info对象来添加你所须要序列化的项
// 同时,将"start"和"end"属性的时间格式转化为格林威治标准时间格式
info.AddValue("start", System.TimeZone.CurrentTimeZone.ToUniversalTime(start));
info.AddValue("end", System.TimeZone.CurrentTimeZone.ToUniversalTime(end));
info.AddValue("interval", interval);
info.AddValue("timeformat", "utc");
}

  这样一来,咱们在系列化过程当中查看"start"属性时就会获得以下结果:

   8/19/2002 9:09:13 PM

  同时请注意咱们在GetObjectData()方法中添加的一个名为"timeformat"的额外属性,经过它咱们能够方便的知道系列化过程当中所使用的时间格式。若是有兴趣的话,你还能够从System.Globalization.DateTimeFormatInfo这个名字空间中获取更多有关时间格式的信息。

  自定义反序列化:

  你能够经过调用一个自定义的构造函数来完成自定义反序列化的操做。该构造函数的定义以下:

public ScheduleCustom (SerializationInfo info,StreamingContext context);

  在上面的类中,咱们的ScheduleCustom()方法将完成把时间格式从格林威治标准时间格式反序列化为当地时间的格式的操做,其函数实现以下:

public ScheduleCustom (SerializationInfo info,StreamingContext context) {
 this.start = info.GetDateTime("start").ToLocalTime();
 this.end = info.GetDateTime("end").ToLocalTime();
 this.interval = info.GetInt32("interval");
}

  在完成自定义序列化和自定义反序列化后,咱们的时间安排程序变成了以下形式:

using System;
using System.Runtime.Serialization;

namespace SerializationSample {
[Serializable()]
 public class ScheduleCustom : System.Runtime.Serialization.ISerializable {
 protected System.DateTime start;
 protected System.DateTime end;
 // 每一个时间间隔所要增长的毫秒数
 protected long interval;

 public System.DateTime Start {get{return start;}set{start=value;}}
 public System.DateTime End {get{return end;}set{end=value;}}
 public long Interval {get{return interval;}set{interval=value;}}

 public ScheduleCustom(System.DateTime Start, System.DateTime End, long Interval) {
  start=Start;
  end=End;
  interval=Interval;
 }

// 若是已经到告终束的时间,则返回结束时间,不然返回下一次运行的时间
public System.DateTime NextRunTime {
 get {
  System.TimeSpan ts = new System.TimeSpan(end.Ticks-System.DateTime.Now.Ticks);
  if(ts.Milliseconds>0) {
   return System.DateTime.Now.AddMilliseconds(interval);
  } else {
   return end;
  }
 } 
}

public void GetObjectData(SerializationInfo info,StreamingContext context) {
 // 运用info对象来添加你所须要序列化的项
 // 同时,将"start"和"end"属性的时间格式转化为格林威治标准时间格式
 info.AddValue("start", System.TimeZone.CurrentTimeZone.ToUniversalTime(start));
 info.AddValue("end", System.TimeZone.CurrentTimeZone.ToUniversalTime(end));
 info.AddValue("interval", interval);
 info.AddValue("timeformat", "utc");
}

public ScheduleCustom (SerializationInfo info,StreamingContext context) {
 this.start = info.GetDateTime("start").ToLocalTime();
 this.end = info.GetDateTime("end").ToLocalTime();
 this.interval = info.GetInt32("interval");
 }
}
}

  四.总结:  本文向你们介绍了.Net框架下系列化机制的一些基本概念和基本的运用方法,读者在读完本文后,应该对如下几个概念有个初步了解:二进制系列化、XML系列化、基本序列化和自定义系列化,并应可以完成一些基本的系列化应用。最后,但愿你们能合理有效的运用系列化机制并发挥它的功效以更好地知足实际工做须要。

相关文章
相关标签/搜索