首页 > 学院 > 开发设计 > 正文

.NET Framework中的串行化操作

2019-11-17 04:42:36
字体:
来源:转载
供稿:网友

  简介

  .NET中的串行化答应编程人员获得一个对象的实例,并将它转换为一种易于在网络上传播或着在数据库、文件系统上存储的格式。该对象实际上是一个定制类型(包括我们已经设置的任何属性或字段)的实例。
我能想到的一些例子包括通过Web服务等向本地或远程应用程序的另一部分发送对象实例的能力。选择串行化的另一个例子是在正常情况下存储在数据库中的对象的数据,但数据的一部分无需个别地进行存储。通过只将需要查询的数据列成单独的字段,数据的其余部份可以在一个定制类型内进行简单的串行化,并在数据库中存储为一个完整的对象。

  下面我们举一个调度对象的例子。这一调度对象能够使应用程序根据一个特定的进度表触发事件。在开发调度对象时,需要考虑许多设置,例如时间间隔、日期和时间限制等。当对于下一个事件应当何时被触发真正重要的信息片断是精确的日期/时间时,正常情况下,数据的每个部份都要求在表中创建一个单独的字段。现在,我们将整个调度对象串行化,并与下一次事件发生的日期/时间一起存储在数据库中,这样,使得整个数据库只包含有3个字段。

  本文包含了可以将对象串行化为的二种格式:xml或二进制,其中每种形式都各有优缺点。本文还论述了串行化的二种方式:基本串行化和定制串行化。最后,文章将以一个实例演示如何通过互联网发送一个定制对象的例子。

  任何串行化处理的第一步都是获取对象的实例,并将它转化为一种内存流。下面的代码演示了完成串行化的4个方法:二进制以及XML的串行化、反串行化。在后面的内容中,我们将论述二进制、XML串行化的区别:

Figure 1.1 核心的串行化方法

#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
  假如想对一个对象进行串行化处理,可以在它的前面添加[Serializable()]标志,给类添加一个定制串行化属性。在下面“Serialization”工程中的Schedule类中,我们会看到这一属性。同时,我们还要考虑名字为[NonSerialized()]的字段属性,它可以用于类中的所有字段,防止它被串行化,假如在类中有一个字段不需要或者我们不希望在正式产品中对某个字段进行串行化处理,这一点是非常有用的。只要简单地在变量前添加NonSerialized()属性即可,因为系统不会自动地对该变量进行串行化处理。

  需要记住的是,[Serializable()]属性适用于整个类,因此假如希望整个类都支持串行化,我们必须添加属性,而[NonSerialized()]属性只适用于被标志为[Serializable()]的类中的字段。使用[NonSerialized()]有时被称作“有选择性的串行化”。

  最后,需要记住的是,为了使用System.Runtime.Serialization.Formatters.Soap.SoapFormatter,我们必须添加System.Runtime.Serialization.Formatters.Soap.dll的引用。

  Figure 1.2 Schedule类


using System;

namespace FifteenSeconds {
///
/// 答应我们提出一个计划
///

[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;
}
}
}
}


} 二进制和XML串行化的比较

  象上面表明的那样,选择XML或二进制串行化不取决于代码的实现。二种方法实现起来都非常简单。下面我们来考虑二种方法各自的优点。

  二进制串行化的优点

  1、无论是否是只读属性,所有成员都将被串行化。

  2、更高的性能。

  XML串行化的优点

  1、对象共享和使用方面更大的灵活性(互操作性)。

  2、不严格的二进制依靠

  3、可读性

  在研究Figure 1.1中的代码后,我们会发现,二进制和XML串行化仅仅依靠于对Formatter的选择。我们还会发现,串行化和反串行化之间的差别就在于是调用Serialize()还是Deserialize()。上面的代码能够原封不动地拷贝到我们的应用程序中,它将成为我们大多数串行化需求的核心
基本串行化和定制串行化的比较

  .NET Framework提供了二种完成串行化的方式。需要记住的是,完成串行化的方式与串行化的格式是二个不同的概念,执行串行化是指.NET Framework如何获得我们对象、以及如何将它转化为最后结果的实际方式,与格式(二进制还是XML格式无关)。最简单的方法是让.NET Framework自动地对对象进行串行化操作,这种方式被称作“基本串行化”。“基本串行化”只要求对象拥有类属性[Serializable()]即可,.NET Framework将获得我们的类,并将它转换为给定的格式。在这种方法中,我们唯一能够施加影响的是,使用[NonSerialized()]字段属性使任何字段式不会被串行化,我们无法对类中的每个字段是如何被串行化的提供精确的控制。

  假如希望对串行化处理有更多的控制,就需要使用“定制串行化”。在这种方式中,我们能够精确地指定哪个字段能够被串行化,以及如何进行串行化。在样例解决方案中,我们创建了一个名字为ScheduleCustom的类,正确地演示了“定制串行化”的用法。

  在使用“基本串行化”时,对象的版本是一个主要问题。要将Schedule对象串行化到磁盘上,然后改变该对象(例如,添加一个字段),在进行反串行化时就会出现问题,得到如下所示的错误信息:

“成员的数量发生错误,FifteenSeconds.Schedule对象有4个成员,被反串行化的成员数量为3。”

  假如碰上这一问题,就要考虑使用“定制串行化”了。由于能够在串行化处理过程中精确地控制类中的所有成员,我们就能够解决因版本而出现的问题。

  定制串行化

  我们需要作的第一件事就是明确地在类前面添加[Serializable()];第二,我们的类必须实现
System.Runtime.Serialization.ISerializable接口,因此,我们的类的定义应当如下所示:

[Serializable()]
public class ScheduleCustom : System.Runtime.Serialization.ISerializable {
 //在接口中,我们能够发现下面的一个方法:
 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值的输出,就会发现它使用了缺省的Framework DateTime格式:

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

  假如要与其他不使用.NET Framework的人合作,并国际化地发布应用程序,用格林威治时间来表示
DateTime值是比较明智的。下面我们来修改GetObjectData()方法,将开始和结束时的值转换为格林威治时间。


public void GetObjectData(SerializationInfo info,StreamingContext context) {
 //使用info对象添加需要串行化的字段
 //为了更好地与别人合作,我们将把时间转换为格林威治时间
 info.AddValue("start", System.TimeZone.CurrentTimeZone.ToUniversalTime(start));
 info.AddValue("end", System.TimeZone.CurrentTimeZone.ToUniversalTime(end));
 info.AddValue("interval", interval);
 info.AddValue("timeformat", "utc");
}
  8/19/2002 9:09:13 PM

  现在,转换时的起始时间将成为如下的格林威治时间形式:

  8/19/2002 9:09:13 PM

  注重,我添加了一个名字为“timeformat”的属性,在以后判定在串行化处理中使用哪种时间格式时,这一字段非凡有用。

  也可以考虑用其他格式输出日期。考虑System.Globalization.DateTimeFormatInfo能够提供哪几种格式,例如,将DateTime转换为自Unix“诞生”(1979年1月1日)后的时间,这样可以更好地与非微软的平台进行集成)。我们也可以在类中添加一个可选的属性,判定在被串行化时DateTime使用的格式。

  定制反串行化

  假如希望使对象能够以定制方式被反串行化,需要使用一个定制构建器。该构建器的定义如下所示:

public ScheduleCustom (SerializationInfo info,StreamingContext context) {}
  我们必须实现这一方法。在schedule类中,我们的实现将把UTC(世界协调时间)转换为本机上的DateTime格式。

public ScheduleCustom (SerializationInfo info,StreamingContext context) {
 //我们将日期/时间转换回本地时间
 this.start = info.GetDateTime("start").ToLocalTime();
 this.end = info.GetDateTime("end").ToLocalTime();
 this.interval = info.GetInt32("interval");
}
  象我们在前面的“定制串行化”过程中指出的那样,添加的timeformat字段能够用来指定如何将串行化的时间转换为本地对象所必需的DateTime格式,但由于在本例中我们只进行了与UTC时间之间的转换,因此是相当简单的。

  使用Web服务的串行化

  在学习新的技术时,最重要的是学习这种技术如何使用。在这一部份中,我们将创建一个有二个方法的Web服务:第一个方法使Framework以二进制的形式返回我们定制串行化的schedule对象,第二个方法以XML的格式返回相同的对象。

  我们只要简单地添加一个带有指向上面的串行化工程引用的新C# Web服务工程即可,然后,我们再创建上面所述的二个方法:

FifteenSeconds.ScheduleCustom customSchedule =
new FifteenSeconds.ScheduleCustom(System.DateTime.Now, System.DateTime.Now.AddHours(5), 10000);

[WebMethod]
public byte[] Get_Binary_Schedule() {
 return FifteenSeconds.Serializer.Serialize( customSchedule,
 FifteenSeconds.Serializer.SerializationFormat.Binary).ToArray();
}
[WebMethod]
public string Get_XML_Schedule() {
 return FifteenSeconds.Serializer.ConvertStreamToString
 (FifteenSeconds.Serializer.Serialize( customSchedule,
 FifteenSeconds.Serializer.SerializationFormat.Xml));
}
  需要注重的是,二个方法都使用了FifteenSeconds.ScheduleCustom对象的同一个实例,在二进制形式中返回byte[]型数据,在XML形式中返回string型数据。

  需要记住的是,Framework将自动地串行化我们的对象,根据使用的方法返回适当类型的数据。另外,我们还需要一些时间研究解决方案中的Serialization工程,在将这些对象串行化为一个文件时,其中有些方法可能会有用。

  结论

  这篇文章演示了在应用程序中使用串行化是如何简单。在需要将一个对象的实例转换为能够方便地传输或保存的格式时,串行化可能就是一种选择。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表