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.序列化时,不建议用自动属性(每次生成的字段可能不一样,影响反序列化)。

 

你可能感兴趣的:(序列化)