【狼窝乀野狼】Serializer妙手回春
在我们很多程序中,需要将数据保存到本地,以便于下次打开还能看到原始数据。例如我们Xmind思维导图,例如我们的Power Designer等等,都是有保存一个隶属于自己的工程文件,那么今天我要说的就是,工程文件中的序列化和反序列化。
【简介】
序列化就是就爱那个对象实例的状态存储到存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以类的名称(包括类的程序集)转为字节流,然后再把字节流写入数据流,在随后对对象进行反序列化的时候,将创建与原来对象相同的副本。
【为什么要序列化?序列化的好处是什么?】
1、将对象的状态保存在存储媒体中,以便于可以在以后重新创建出来完全相同副本。
2、按值将对象从一个应用程序域发送到另一个应用程序域。这样可以方便内存对象变成磁盘数据,便于网络传输,用于分布式,WCF等应用(JSON其实就是将对象序列化为字符串)。实现对象的备份和还原。
【持久储存】
我们经常需要将对象的字段值保存到磁盘中,并在以后检索这些数据。尽管我们不采用序列化也可以做到这事儿,但是通常这种方法比较繁琐而且容易出现差错。在需要跟踪对象的层次结构时,会变得越来越复杂。可以想象如果我们在进行编写一个包含大量对象的大型的业务应用程序的时候,开发人员不得不为每个对象编写代码,以便于将字段和属性保存到磁盘中以及从磁盘中还原这些对象和属性。而序列化提供了轻松快捷实现这个目标的方法。
【序列化种类】
一种是二进制序列化。将对象转为二进制文件。
二种是XML序列化。将对象转为XML文件的方式。
二者之间不同于,前者将所有的成员变量(包含 private,protected,public)字段或者属性进行序列化;后者只将公有字段和属性进行序列化。
【序列化方式】
想要将一个类进行序列化,首先需要将其标记可序列化([Serializable])。通常我们采用两种方式进行序列化,一种是自动序列化,只需要在类名头上面添加[Serializable]特性标签即可。另一种是手动序列化,需要添加[Serializable]特性标签并且实现ISerializable接口。
自动序列化的好处在于,我们可以人为地将一些字段或者属性进行序列化。当然在自动序列化过程中,只要我们将不需要序列化的字段或者属性标记上[NonSerialized],那么这个字段或者属性,就不会被序列化。
自动序列化,系统是采用"<FileName>k__BackingField"
例如上面一个数据建模,在自动序列化中
1 [Serializable] 2 public class Person 3 { 4 public int Age { get; set; } 5 6 public string Name { get; set; } 7 }
在自动序列化文件中,存在形式如下:
手动序列化
1 [Serializable] 2 public class Animal:ISerializable 3 { 4 public int Age { get; set; } 5 6 public string Name { get; set; } 7 8 public Animal() 9 { 10 11 } 12 13 private Animal(SerializationInfo info, StreamingContext context) 14 { 15 Age = info.GetInt32("Age"); 16 Name = info.GetString("Name"); 17 } 18 19 public void GetObjectData(SerializationInfo info, StreamingContext context) 20 { 21 info.AddValue("Age", Age); 22 info.AddValue("Name", Name); 23 } 24 }
通过两种方式对比,我们看出,手动序列化,可以指定我们一个属性或者字段在磁盘中映射名称。
【工程兼容问题】
既然我们在做开发,那么就可能因为业务的需要,对以前的数据建模进行修改。例如在原来的建模中添加一个属性等。如果添加一个属性,那么在反序列化的时候就会出现问题,导致打开工程失败。原因在于,例如以前序列化的一个对象,里面包含了三个字段,但是在反序列化的时候,你需要反序列化四个字段,那么就会导致失败。
做工程兼容,首先需要查看以前的建模是采用自动序列化还是手动序列化。如果是自动序列化,你需要将自动序列化变成手动序列化,其次我们需要判断里面某个属性存在不。
1 [Serializable] 2 public class Animal:ISerializable 3 { 4 public int Age { get; set; } 5 6 public string Name { get; set; } 7 8 public Animal() 9 { 10 11 } 12 13 private Animal(SerializationInfo info, StreamingContext context) 14 { 15 //Age = info.GetInt32("Age"); 16 //Name = info.GetString("Name"); 17 var serializedData = GetSerializedData(info); 18 var currentData = GetSerializableData(); 19 foreach (var memberInfo in serializedData) 20 { 21 var val = memberInfo.Value; 22 if (val == null) 23 { 24 continue; 25 } 26 if (currentData.ContainsKey(memberInfo.Key)) 27 { 28 currentData[memberInfo.Key](val); 29 } 30 } 31 } 32 33 public void GetObjectData(SerializationInfo info, StreamingContext context) 34 { 35 info.AddValue("Age", Age); 36 info.AddValue("Name", Name); 37 } 38 39 /// <summary> 40 /// 当前类中字段或者属性射影 41 /// </summary> 42 /// <returns></returns> 43 private Dictionary<string, Action<object>> GetSerializableData() 44 { 45 return new Dictionary<string, Action<object>> 46 { 47 {"Age",value=>Age = (int)value}, 48 {"Name",value=>value.ToString()} 49 }; 50 } 51 52 /// <summary> 53 /// 序列化中,字段或者属性的射影 54 /// </summary> 55 /// <param name="info"></param> 56 /// <returns></returns> 57 private Dictionary<string, object> GetSerializedData(SerializationInfo info) 58 { 59 var serializs = info.GetEnumerator(); 60 Dictionary<string, object> dic = new Dictionary<string, object>(); 61 while (serializs.MoveNext()) 62 { 63 dic.Add(serializs.Current.Name, serializs.Current.Value); 64 } 65 return dic; 66 } 67 }
如果新的需求,需要在建模中添加字段或者属性,我们只需要,在GetObjectData中添加需要序列化的字段,在GetSerializableData,添加反序列化的处理。这样,不管添加多少个字段,我们都可以在新的工程中打开以前的数据。
【总结】
技术不在于牛不牛,只在于问题你遇见过没有,处理成功没有。当一天你遇到问题足够多,解决问题足够多,你也成为了站在塔尖上耀眼的那个人。今天的内容有点粗糙,可能以前可有人板书过的,可能我在这里有点画蛇添足,而且笔锋也在有点飘逸零乱,下次再将我们如果在程序中修改了程序集名称,如何处理兼容性问题。