C#中如何实现序列化

感谢zhoufoxcn 和 zzxap 的帖子。http://topic.csdn.net/u/20090327/10/2559468d-9fbf-4caa-944e-d992f9609ba9.html

 

序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。例 如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。反之,反序列化根据流重新构造对象。此外还可以将对象序列化后保存到本地,再次运行的时候可以从本地文件中“恢复”对象 到序列化之前的状态。
在.net中有提供了几种序列化的方式:
二进制序列化
XML序列化
SOAP序列化

 

C# code
       
       

一个例子,以显示如何正确的运用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();

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

  
// 创建一个文件"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 " );
 }
}
}

 
 

 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值