C# 序列化

     所谓序列化,就是将对象(数据)换一种方式存储。在C#中有json序列化(JavaScriptSerializer)、Xml序列化(XmlSerializer)和二进制序列化(BinaryFormatter),因为序列化是对数据的存储,所有类中的方法、继承关系、接口它都不会存储,故序列化指的是,序列化对象中的属性(与访问修饰符无关,即便是private的属性也能被序列化,这些都是有前提的,下面会提及)还有3种形式的反序列化,下面分别给予介绍:

首先定义一个很普通的Person类:

1 public class Person
2     {
3         public string Name { get; set; }
4         public int Age { get; set; }
5         public string Gender { get; set; }
6         public string Telephone { get; set; }
7     }

1.json序列化(JavaScriptSerializer)

->json序列化,就是将对象序列化成一个字符串(便于对象的存储和传输)。使用步骤是先创建一个JavaScriptSerializer对象,再执行序列化:

1             //对象初始化器
2             Person p1 = new Person() { Name = "张三", Age = 18, Gender = "", Telephone = "13800138000" };
3             JavaScriptSerializer jsSer = new JavaScriptSerializer();
4             string s = jsSer.Serialize(p1);
5             Console.WriteLine(s);
6             Console.ReadKey();

运行结果如下:

->json反序列化,所谓反序列化就是序列化的逆过程,将上面代码中序列化的字符串s反序列化成Person对象。

1             Person p1 = new Person() { Name = "张三", Age = 18, Gender = "", Telephone = "13800138000" };
2             JavaScriptSerializer jsSer = new JavaScriptSerializer();
3             string s = jsSer.Serialize(p1);
4 
5             //执行反序列化
6             object o = jsSer.Deserialize(s, typeof(Person));
7             Person p2 = o as Person;
8             Console.WriteLine(string.Format("姓名是:{0},年龄:{1},性别:{2},联系电话是:{3}",p2.Name,p2.Age,p2.Gender,p2.Telephone));

运行结果如下:

2.Xml序列化(XmlSerializer)

顾名思义,xml序列化就是将对象序列化成xml格式来存储。

1             Person p1 = new Person() { Name = "张三", Age = 18, Gender = "", Telephone = "13800138000" };
2             XmlSerializer xmlSer = new XmlSerializer(typeof(Person));
3             using (FileStream fsWrite = new FileStream("person.xml", FileMode.CreateNew, FileAccess.Write))
4             {
5                 xmlSer.Serialize(fsWrite, p1);
6             }
7             Console.WriteLine("xml序列化完毕");
8             Console.ReadKey();

运行上面的程序,弹窗后,会在执行程序集中多一个person.xml的文件,内容中数据的存储格式如下:

<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>张三</Name>
  <Age>18</Age>
  <Gender>男</Gender>
  <Telephone>13800138000</Telephone>
</Person>

再进行一个xml反序列化,代码如下:

1             XmlSerializer xmlSer = new XmlSerializer(typeof(Person));
2             using (FileStream fsRead=new FileStream("person.xml",FileMode.Open,FileAccess.Read))
3             {
4                 object o = xmlSer.Deserialize(fsRead);
5                 Person p2 = o as Person;
6                 Console.WriteLine(string.Format("姓名是:{0},年龄:{1},性别:{2},联系电话是:{3}", p2.Name, p2.Age, p2.Gender, p2.Telephone));
7             }

结果与json反序列化结果一致:

json序列化和xml序列化的异同点:

1.两者都是将对象以不同的格式存储;

2.json序列化是将对象序列化成字符串放在内存中;而xml序列化是将对象序列化成xml格式,并最终存储到磁盘文件中。

json反序列化和xml反序列化的异同点:

1.两者都是将序列化后的结果进行还原成原来的对象,需要注意的是,因序列化是对数据的存储,所有类中的方法、继承关系、接口它都不会存储,故序列化指的是,序列化对象中的属性(数据),反序列化也只能看到对象的数据,类中的方法、继承关系、接口等是不会在反序列化中体现的。

2.json反序列化是将json字符串“还原”成Person对象,而xml反序列化是从存储的文件中读取数据,“还原”成Person对象(反序列化后的数据都会放在内存中)。

3.二进制序列化(BinaryFormatter)

将对象(比如Person对象)转换为二进制数据(字节流)的过程。与上面2种序列化不同的是,二进制序列化要求被序列化的对象被标记为“可序列化的”(类型前加[Serializable])

1     [Serializable]
2     public class Person
3     {
4         public string Name { get; set; }
5         public int Age { get; set; }
6         public string Gender { get; set; }
7         public string Telephone { get; set; }
8     }

二进制序列化对象的代码如下:

1 Person p1 = new Person() { Name = "张三", Age = 18, Gender = "", Telephone = "13800138000" };
2             BinaryFormatter bf = new BinaryFormatter();
3             using (FileStream fsWrite=new FileStream("1.txt",FileMode.Create,FileAccess.Write))
4             {
5                 bf.Serialize(fsWrite, p1);
6             }
7             Console.WriteLine("二进制序列化完毕");
8             Console.ReadKey();

打开1.txt,大部分都无法看懂,依稀能看到Person类的属性。
下面看一下二进制反序列化:

1             BinaryFormatter bf = new BinaryFormatter();
2             using (FileStream fsRead=new FileStream("1.txt",FileMode.Open,FileAccess.Read))
3             {
4                 object o = bf.Deserialize(fsRead);
5                 Person p2 = o as Person;
6                 Console.WriteLine(string.Format("姓名是:{0},年龄:{1},性别:{2},联系电话是:{3}", p2.Name, p2.Age, p2.Gender, p2.Telephone));
7             }
8             Console.ReadKey();

执行结果如下:

在进行二进制反序列化时,需要注意的是,当把二进制序列化后的文件拷贝到另外一个程序中进行反序列化时,执行二进制反序列化的程序必须引用序列化文件所在的程序集,否则会报错。这是什么原因呢?是因为在进行二进制序列化时,只序列化了类中的属性,类中的其他信息:继承关系、类中的方法、接口等成员均不被序列化,引用序列化文件所在的程序集可以还原这些成员

关于二进制序列化的一些其他问题:

1.还是上面的Person类,代码如下:

 1     [Serializable]
 2     public class Person
 3     {
 4         public car BenCi { get; set; }
 5         public string Name { get; set; }
 6         public int Age { get; set; }
 7         public string Gender { get; set; }
 8         public string Telephone { get; set; }
 9     }
10 
11     public class car
12     {
13 
14     }

序列化代码如下:

1             Person p1 = new Person() { Name = "张三", Age = 18, Gender = "", Telephone = "13800138000",BenCi=new car() };
2             BinaryFormatter bf = new BinaryFormatter();
3             using (FileStream fsWrite = new FileStream("1.txt", FileMode.Create, FileAccess.Write))
4             {
5                 bf.Serialize(fsWrite, p1);
6             }
7             Console.WriteLine("二进制序列化完毕");

上面的代码编译没问题,运行就会报错,这是因为属性BenCi的类型car不是可序列化的,将car类标记为可序列化的即可通过。即:

1    [Serializable]
2     public class car
3     {
4 
5     }

要求被序列化的对象的类型中所有属性(字段)的类型也必须标记为“可序列化的”。

2.定义一个父类,让Person类继承至它。

 1     public class Animal
 2     {
 3     
 4     }
 5 
 6     [Serializable]
 7     public class Person : Animal
 8     {
 9         public car BenCi { get; set; }
10         public string Name { get; set; }
11         public int Age { get; set; }
12         public string Gender { get; set; }
13         public string Telephone { get; set; }
14     }

序列化代码不变:

1             Person p1 = new Person() { Name = "张三", Age = 18, Gender = "", Telephone = "13800138000",BenCi=new car() };
2             BinaryFormatter bf = new BinaryFormatter();
3             using (FileStream fsWrite = new FileStream("1.txt", FileMode.Create, FileAccess.Write))
4             {
5                 bf.Serialize(fsWrite, p1);
6             }
7             Console.WriteLine("二进制序列化完毕");

运行时会报错,这是因为,虽然Person标记为可序列化的,但其父类Animal不是可序列化的一个对象要执行序列化,不止它要标记为可序列化的,它的所有父类也必须标记为“可序列化的”,查看所有类型的父类Object,微软就将标记为Serializable,故Object是“祖宗类”,所有它的子类都可以被序列化。

3.在Person类中,如果不想让其中某个属性序列化,该怎么做呢?

1         [NonSerialized]
2         private string name = null;
3         public string Name
4         {
5             get { return this.name; }
6             set { this.name = value; }
7         }

也就是说,不参与序列化的属性不能是自动属性,要写成上面的样子,且在字段前标记为NonSerialized。这样,序列化后的结果中就不包含该属性了。

4.请看下面的代码,给Person类加下面这个方法:

1         public Animal GetAnimal(Animal ani)
2         {
3             return new Animal();
4         }

其中Animal类并未标记为序列化,为什么编译和运行时均不会报错呢?因为序列化时类中的方法不参与序列化,所以方法的返回值和参数类型是不是可序列化的没有关系

总结:

 1.序列化,将对象换一种方式存储。
 2.只序列化数据(类的属性),不序列化类中的方法。
 3.一个对象要执行序列化,不止它要标记为可序列化的,它的所有父类也必须标记为“可序列化的”。
 4.要求被序列化的对象的类型中所有属性(字段)的类型也必须标记为“可序列化的”。
 5.类中的方法不参与序列化,所以类中的方法无需标记序列化。
 6.类中的一个属性如果不想被序列化,那么要在其对应的字段前加NonSerialized.且属性不能简写。
 7.反序列化时,必须找到对象序列化时原来的程序集(重新还原对象,而序列化文件中只包含哪些数据信息,不包含该对象的类型相关信息:类中有哪些方法、接口,父类是谁)。
 8.序列化时,不建议用自动属性(每次生成的字段可能不一样,影响反序列化)。

原文地址:https://www.cnblogs.com/chens2865/p/3887309.html