序列化Serialize&DeSerialize
序列化就是将C#的类型、对象序列化为字节流,反序列化则是反其道而行,将字节数据反转成C#类型。序列化的作用主要在于传输数据,而字节数据的格式是各种程序都可以通用的传输形式。C#类型默认不能被序列化,除非在类型上使用Serializable特性(Serializable在System命名空间中被定义),以表明这是一个可以被序列化的类型。但字符串、集合、数组默认都可以序列化,也即这几种类型不用为其添加Serializable特性。具体实施序列化操作的类叫做BinaryFormatter,它提供Serialize和Deserialize将对象序列化到流中存储,BinaryFormatter会自动搜索运用在可序列化类型上的各种序列化(反序列化)特性,根据特性的描述从而对类型进行相应的格式化,这就是为什么需要序列化对象时,你需要使用一些与序列化有关的特性,下面是常用的几个序列化特性。
1.Serializable特性
BinaryFormatter format = new BinaryFormatter();
MemoryStream stream = new MemoryStream() ;
format.Serialize(stream, array); //序列化
stream.Position = 0; //此处需要将指针归位,否则抛错
int[] newarray=format.Deserialize(stream) as int[]; //反序列化
foreach (var item in newarray)
{
Console.WriteLine(item);
}
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleApp1
{
[Serializable]
public class Animal
{
private string Name = "sam";
public string GetName()
{
return Name;
}
}
class Program
{
static void Main(string[] args)
{
//序列化
Animal a = new Animal();
BinaryFormatter format = new BinaryFormatter();
MemoryStream stream = new MemoryStream() ;
format.Serialize(stream, a);
stream.Position = 0;
//反序列化
object obj= format.Deserialize(stream);
stream.Position = 0;
Animal newa = obj as Animal;
Console.WriteLine(newa.GetName());
}
}
}
2.NonSerialized特性
序列化会无视private或protected权限修饰符,如果不希望某些成员被序列化,可以在成员上使用NonSerialized特性
public class Document
{
public string Title;
public string Content;
[NonSerialized]
private long Pass;
}
3.OptionalFieldAttribute特性
如果为可被序列化的类型新增了一个成员,但在未新增成员之前已经序列化这个类型的对象到某个文件,那么当反序列化这个文件时,BinaryFormatter会因为在查找到类型的新成员与文件保存的序列化数据不匹配从而抛出异常。要避免这样的情况,可以在新增成员上运用OptionalFieldAttribute特性,BinaryFormatter搜索到新成员时会发现它运用了此特性,这样,它将自动忽略这个新成员。注意,这个特性在.net framework2.0之前不可用,在这之前的解决办法可参考C#本质论17章(497页)。
4.OnSerializing | OnSerialized | OnDeserializing | OnDeserialized特性
这四个特性分别表示:序列化时、序列化完成后、反序列化时、反序列化完成后。也即你可以定义处理方法,并为方法运用这几个特性。一旦运用了这些特性,你的方法将像事件那般可以被自动触发,这样做的优点在于,你可以掌控序列化或反序列化的进行时或完成后的操作。这些特性都会在相应的语义下自动完成对方法的调用,并把方法的逻辑运用在序列化或反序列化后的对象上。比如当一个 属性被运用了NonSerialized之后,它将被序列化忽视,如果再次将序列化的字符反序列化为对象时,未被序列化的属性就得不到正确的值,但通过OnDeserialized特性就可以在反序列化完成后将未被序列化的属性的值运用到反序列化后的对象上。
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleApp1
{
[Serializable] //标识为可被序列化/反序列化的特性类
public class Document
{
public string Title;
public string Content;
[NonSerialized]
public long Pass;
[System.Runtime.Serialization.OnSerializing()] //序列化时将执行此方法
public void OnSerializingMethod(System.Runtime.Serialization.StreamingContext context)
{
}
[System.Runtime.Serialization.OnSerialized()] //序列化完成后将执行此方法
public void OnSerializedMethod(System.Runtime.Serialization.StreamingContext context)
{
}
[System.Runtime.Serialization.OnDeserializing()] //反序列化时将执行此方法
public void OnDeserializingMethod(System.Runtime.Serialization.StreamingContext context)
{
}
[System.Runtime.Serialization.OnDeserialized()] //反序列化完成后将执行此方法
public void OnDeserializedMethod(System.Runtime.Serialization.StreamingContext context)
{
Pass = 123;
}
}
class Program
{
static void Main(string[] args)
{
Stream stream;
Document doc = new Document { Title = "xxx", Content = "yyy", Pass = 123 };
string fileName = @"D:\test.txt";
stream = File.Open(fileName, FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, doc);//将Document对象序列化,但Pass未参与序列化
stream.Dispose();
stream = File.Open(fileName, FileMode.Open);
Document doc2 = (Document)formatter.Deserialize(stream); //反序列化完成后将执行OnDeserializedMethod,动态将Pass运用在doc2上
Console.WriteLine(doc2.Title + doc2.Content + doc2.Pass);
}
}
}
5.ISerializable接口
如果你想实现自定义的序列化逻辑,比如内置的序列化功能无法对数据实现加密,而通过使可序列化类型实现ISerializable就可以实现这个效果。此接口提供了GetObjectData方法,还有两个重要的参数,SerializationInfo(提供序列化信息的对象)、StreamingContext(序列化上下文对象)。SerializationInfo提供AddValue方法将类型的成员添加到字典集合,提供GetString方法获取某个成员的值。你只需要在GetObjectData里实现对密码的加密,在可序列化的类的构造函数里也接收SerializationInfo和StreamingContext作为参数,这个构造函数用于实现对密码的解密。
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleApp1
{
[Serializable]
public class XDocument : ISerializable
{
public enum Field { Title, Pass }
public string Title;
public string Pass;
//加密
public static string Encrypt(string data)
{
return "@#$%";
}
//解密
public static string Decrypt(string data)
{
return "123";
}
public XDocument() { }
//在构造函数里实现对密码的解密
public XDocument(SerializationInfo info, StreamingContext context)
{
Title = info.GetString(Field.Title.ToString());
Pass = Decrypt(info.GetString(Field.Pass.ToString()));
}
//ISerializable的方法,当对象被序列化时会自动执行这个方法
//在这个方法里实现加密
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue(Field.Title.ToString(), Title);
info.AddValue(Field.Pass.ToString(), Encrypt(Pass));
}
}
class Program
{
static void Main(string[] args)
{
MemoryStream ms = new MemoryStream();
XDocument doc = new XDocument { Title = "xxx", Pass = "123" };
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, doc);
ms.Position = 0;
object o= formatter.Deserialize(ms);
Console.WriteLine((o as XDocument).Pass);
}
}
}