【C#面向对象】第九课——C#中的序列化和反序列化

知识点:理解序列化和反序列化的概念、掌握二进制序列化和反序列化的方法、掌握 XML 序列化和反序列化的方法。

 

1、序列化和反序列化概述

1.1   什么是序列化和反序列化

        序列化(Serialization)是通过将对象转换为字节流,从而存储对象或将对象传输到内存,数据库或文件的过程。主要用途是保存对象的状态,包括对象的数据,以便能够在需要是重建对象。反向过程称为 反序列化(Deserialization)。序列化和反序列化都是操作整个对象,和对象属性的多少没有关系。

【C#面向对象】第九课——C#中的序列化和反序列化_第1张图片

        如上图所示,对象 object 被序列化为 流,其中不仅包含数据、还包含对象类型的相关信息,如版本、区域性和程序集名称。然后可以将此流中的内容存储到数据库、文件或内存中。

 

1.2   为什么要进行序列化和反序列化?

        .Net程序执行时,对象都驻留在内存中;内存中的对象如果需要传递给其他系统使用;或者在关机时需要保存下来以便下次再次启动程序使用就需要序列化和反序列化。

        序列化最重要的作用:在传递和保存对象时.保证对象的完整性和可传递性。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。

        反序列化的最重要的作用:根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。

        总结:核心作用就是对象状态的保存和重建。(整个过程核心点就是字节流中所保存的对象状态及描述信息)

 

1.3   序列化的好处

  • 实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里)。
  • 利用序列化实现远程通信,即在网络上传送对象的字节序列。
  • 通过序列化可以在进程间传递对象。

 

1.4   实现序列化的方法

        我们可以把对象序列化为不同的格式,比如说,Json序列化、XML序列化、二进制序列化、SOAP序列化等,以上这些不同的格式也都是为了适应具体的业务需求。

C#实现序列化的方法:

  • 二进制(流)序列化(BinaryFormatter)
  • XML序列化
  • SOAP序列化(SoapFormatter)

        二进制(流)序列化,可以保持一个对象所有的数据和类型信息,对于在应用程序的不同调用之间保留对象的状态很有用。

         XML 序列化可提高可读性,以及对象共享和使用的灵活性,XML 序列化将对象的公共字段和属性或方法的参数和返回值序列化成符合特定 XML 格式的流, System.Xml.Serialization 包含序列化和反序列化 XML 所需要的类。XML 是开放的标准,要 在不同的系统之间共享数据是个很好的选择。

         BinaryFormatter能够对所有字段(私有、保护、公有)进行序列化和反序列化,而XmlSerializer仅序列化公共属性和字段,不保留类型信息。

 

2、二进制(流)序列化

2.1  二进制(流)序列化

        二进制(流)序列化是一种将数据写到输出流,以使它能够用来自动重构成相应对象的机制。二进制,其名字就暗示它的必要信息是保存在存储介质上,而这些必要信息要求创建一个对象的精确的二进制副本。在二进制(流)序列化中,整个对象的状态都被保存起来。为了使用二进制序列化,我们需要引入System.Runtime.Serialization.Formatters.Binary名字空间。

C#中进行二进制(流)序列化的基本步骤:

(1)创建文件流:FileStream 文件流对象 = new FileStream(string filePath,FileMode mode);

(2)创建二进制格式化器:BinaryFormatter 二进制格式化器对象 = new BinaryFormatter();

(3)序列化对象:二进制格式化器对象.Serialize(文件流,被序列化对象);

(4)关闭文件流:文件流对象.Close();

注意:使用二进制(流)序列化时,必须在需要序列化的类前面加上 [Serializable] 特性,该特性对类进行描述,表示该类可以进行序列化,代码格式如下:

[Serializable]
public class Person
{...}

 

示例练习1:写入文本文件

第一步,新建窗体项目:打开Visual Studio软件后,单击【文件】——【新建】——【项目】,在新建项目对话框中选择【Window 窗体应用(.NET Framework)】,这里使用默认名称【WindowsFormsApp2】,然后单击【确定】。

第二步,添加Button控件:根据需要,在窗体Form1上,添加一个Button控件,并设置它的Name属性为:btnSerialize ,Text属性为:二进制序列化

【C#面向对象】第九课——C#中的序列化和反序列化_第2张图片

第三步,添加Person类:在当前项目中,右键单击项目名称,依次点击【添加】→【类】,类名设置为Person.cs。设置Person类的 [Serializable] 特性。

【C#面向对象】第九课——C#中的序列化和反序列化_第3张图片

第四步,设置Person类:给Person类添加属性、定义方法

【C#面向对象】第九课——C#中的序列化和反序列化_第4张图片

namespace WindowsFormsApp2
{
    [Serializable] //给Person类添加该特性,表示Person类可以进行序列化
    public class Person
    {
        public Person(string name, int age, string gender)
        {
            this.Name = name;
            this.Age = age;
            this.Gender = gender;
        }
        public Person() { }
        public string Name { get; set; }
        public int Age { get; set; }
        public string Gender { get; set; }
        public string SayHello()
        {
            return string.Format("大家好,我叫{0},年龄{1},性别{2}", Name, Age, Gender);
        }
    }
}

第五步,给按钮添加Click事件:回到Form1窗体设计界面,双击按钮【二进制序列化】,进入该按钮的点击事件,编写代码。

【C#面向对象】第九课——C#中的序列化和反序列化_第5张图片

        private void btnSerialize_Click(object sender, EventArgs e)//二进制序列化按钮事件处理
        {
            Person person = new Person("王迪", 18, "女");//声明一个Person对象,用来进行保存
            //二进制序列化
            //①创建文件流
            FileStream fileStream = new FileStream(@"D:\shu\person.bin", FileMode.Create);//需要引入命名空间System.IO;
            //②创建二进制格式化器
            BinaryFormatter formatter = new BinaryFormatter();//需要引入命名空间System.Runtime.Serialization.Formatters.Binary;
            //③序列化对象
            formatter.Serialize(fileStream, person);
            //④关闭文件流
            fileStream.Close();
        }

第六步,运行测试:点击运行,在打开的窗体上点击按钮【二进制序列化】,然后打开指定盘符,会发现新生成了一个二进制文件person.bin。

【C#面向对象】第九课——C#中的序列化和反序列化_第6张图片       【C#面向对象】第九课——C#中的序列化和反序列化_第7张图片

注意:在使用二进制格式序列化时,必须给要序列化的类添加[Serializable] 特性,否则会报异常:

【C#面向对象】第九课——C#中的序列化和反序列化_第8张图片

 

2.2  二进制(流)反序列化

        示例1 运行后产生的二进制文件是无法读取的,只有将其转换为计算机能够识别的格式才有作用。 那么现在我们用反序列化的方法,将二进制文件中的数据还原成 Person 对象。

二进制反序列化的步骤:

(1)创建文件流

(2)创建二进制格式化器

(3)反序列化获得对象

(4)关闭文件流

二进制反序列化方法: 对象类型对象 = (对象类型)二进制格式化器.Deserialize(文件流);

Deserialize 方法返回的是 Object 类型,所以需要类型转换。

 

示例练习2:二进制格式反序列化

要求:正在示例1 的基础上,添加一个按钮,对二进制文件进行反序列化

第一步,添加Button按钮:在示例1 的基础上,打卡Form1窗体设计界面,从工具箱里拖一个Button控件,并设置它的Name属性为:btnDeserialize ,Text属性为:二进制反序列化

【C#面向对象】第九课——C#中的序列化和反序列化_第9张图片

第二步,给按钮添加Click事件:双击按钮【二进制反序列化】,进入该按钮的点击事件,编写代码。

【C#面向对象】第九课——C#中的序列化和反序列化_第10张图片

        private void btnDeserialize_Click(object sender, EventArgs e)//二进制反序列化按钮事件处理
        {
            //创建文件流
            FileStream fileStream = new FileStream(@"D:\shu\person.bin", FileMode.Open);
            //创建二进制格式化器
            BinaryFormatter formatter = new BinaryFormatter();
            //反序列化还原对象
            Person person = (Person)formatter.Deserialize(fileStream);
            //关闭文件流
            fileStream.Close();
            MessageBox.Show(person.SayHello());//消息框弹出,弹出内容Person类的SayHello方法
        }

第三步,运行测试:点击运行,在打开的窗体上点击按钮【二进制反序列化】,弹出消息框,调用方法,输出自我介绍内容。

【C#面向对象】第九课——C#中的序列化和反序列化_第11张图片

 

2.3  NoSerialized 特性

        默认情况下,二进制序列化会保存所有对象的字段和属性,如果不想让某个字段被序列化,我们可以在字段前加上[NoSerialized] 特性,这样序列化时就不会保存该字段,[NoSerialized] 只能标记字段

 

示例练习3:使用[NoSerialized]特性

要求:修改 Person 类,给 gender 字段添加[NoSerialized]特性。

第一步,修改Person类:在示例1 的基础上,打开Person.cs文件,给 gender 字段添加[NoSerialized]特性。

【C#面向对象】第九课——C#中的序列化和反序列化_第12张图片

namespace WindowsFormsApp2
{
    [Serializable] //给Person类添加该特性,表示Person类可以进行序列化
    public class Person
    {
        public Person(string name, int age, string gender)
        {
            this.Name = name;
            this.Age = age;
            this.Gender = gender;
        }
        public Person() { }
        public string Name { get; set; }
        public int Age { get; set; }
        [NonSerialized]
        private string gender;//给字段gender,设置[NonSerialized]特性
        public string Gender    //设置属性Gender的get和set
        {
            get { return gender; }
            set { gender = value; }
        }
        public string SayHello()
        {
            return string.Format("大家好,我叫{0},年龄{1},性别{2}", Name, Age, Gender);
        }
    }
}

第二步,运行测试:点击运行按钮,在弹出的窗体中,先点击【二进制序列化】按钮,再点击【二进制反序列化】按钮,查下效果,对比示例2。

【C#面向对象】第九课——C#中的序列化和反序列化_第13张图片

案例分析:其它属性照常输出,但是性别属性为空,是因为 gender 字段没有保存到文件中。

 

3、XML 格式序列化和反序列化

3.1  XML 格式序列化

        使用XML序列化需要引入命名空间System.Xml.Serialization。

XML格式序列化的基本步骤:

(1)创建文件流

(2)创建 XML 序列化器

(3)序列化对象

(4)关闭文件流

创建 XML 序列化器: XmlSerializer serializer = new XmlSerializer(对象类型); 对象类型可以用 typeof 运算符获得,如:typeof(Person)。

序列化对象:serializer.Serialize(文件流, 对象);

 

示例练习4:XML 格式的序列化

要求:点击【XML序列化】按钮后,将对象保存到 XML 文件中

​​​​​​​第一步,添加Button按钮:在示例1 的基础上,打开Form1窗体设计界面,从工具箱里拖一个Button控件,并设置它的Name属性为:btnXmlSerialize ,Text属性为:XML序列化

【C#面向对象】第九课——C#中的序列化和反序列化_第14张图片

第二步,给按钮添加Click事件:双击按钮【XML序列化】,进入该按钮的点击事件,编写代码。

【C#面向对象】第九课——C#中的序列化和反序列化_第15张图片

        private void btnXmlSerialize_Click(object sender, EventArgs e)//XML序列化按钮事件处理
        {
            Person person = new Person("张三", 21, "男");//声明一个Person对象,用来进行保存
            //XML格式序列化
            //①创建文件流
            FileStream fileStream = new FileStream(@"D:\shu\person.xml", FileMode.Create);
            //②创建XML格式化器
            XmlSerializer serializer = new XmlSerializer(typeof(Person));//需要引入命名空间System.Xml.Serialization;
            //③序列化对象
            serializer.Serialize(fileStream, person);
            //④关闭文件流
            fileStream.Close();
        }

第三步,运行测试:点击运行,在打开的窗体上点击按钮【XML序列化】,然后打开指定盘符,会发现新生成了一个 XML 文件person.xml。

【C#面向对象】第九课——C#中的序列化和反序列化_第16张图片

查看XML文件:

【C#面向对象】第九课——C#中的序列化和反序列化_第17张图片

案例思考:在使用XML格式序列化时,类是否需要添加[Serializable] 特性?验证一下代码。

 

3.2 XML 格式反序列化

XML 格式反序列化的步骤:

(1)创建文件流

(2)创建 XML 格式化器

(3)反序列化获得对象

(4)关闭文件流

示例练习5:XML 格式反序列化

要求:点击“XML 反序列化”按钮后,显示保存的 Person 对象信息。

​​​​​​​第一步,添加Button按钮:在示例1 的基础上,打开Form1窗体设计界面,从工具箱里拖一个Button控件,并设置它的Name属性为:btnXMLDeserialize ,Text属性为:XML反序列化

【C#面向对象】第九课——C#中的序列化和反序列化_第18张图片

第二步,给按钮添加Click事件:双击按钮【XML反序列化】,进入该按钮的点击事件,编写代码。

【C#面向对象】第九课——C#中的序列化和反序列化_第19张图片

        private void btnXMLDeserialize_Click(object sender, EventArgs e)
        {
            FileStream fileStream = new FileStream(@"D:\shu\person.xml", FileMode.Open);//①创建文件流
            XmlSerializer serializer = new XmlSerializer(typeof(Person));//②创建XML格式序列化器
            Person person = (Person)serializer.Deserialize(fileStream);//③反序列化获得对象
            MessageBox.Show(person.SayHello());//弹出消息框,输出对象的方法
            fileStream.Close();//④关闭文件流
        }

第三步,运行测试:点击运行,在打开的窗体上点击按钮【XML反序列化】,弹出消息框,调用方法,输出自我介绍内容。

【C#面向对象】第九课——C#中的序列化和反序列化_第20张图片

案例思考:使用XML序列化时忽略某个字段或属性,要如何操作?(让弹出框信息同示例3 类似)

解决办法:可以使用[XmlIgnore]来标志想要忽略的字段或属性。

注意事项:①使用[XmlIgnore]时需要引入命名空间System.Xml.Serialization;   如果不引入命名空间 也可以直接使用[System.Xml.Serialization.XmlIgnore]  ②[XmlIgnore]只能标志公共属性或字段。

 

3.3   自定义 XML 文件

         在示例5 中生成的 person.xml 文件结构和 Person 类的结构一致,XML 的一个重要特性就是可以自由定义其中的节点,那么我们能否控制生成的 XML 文件结构呢? 通过 C#提供的特性我们可以自定义生成 XML 文件的结构。

 

示例练习6:自定义 XML

第一步,修改Person类:在示例1 的基础上,打开Person.cs文件,进行修改。

【C#面向对象】第九课——C#中的序列化和反序列化_第21张图片

namespace WindowsFormsApp2
{
    [XmlRoot("个人信息")]//定义根节点,将默认的Person改为“个人信息”
    public class Person
    {
        public Person(string name, int age, string gender)
        {
            this.Name = name;
            this.Age = age;
            this.Gender = gender;
        }
        public Person() { }
        [XmlAttribute("姓名")]//定义xml属性,需要引入命名空间
        public string Name { get; set; }
        public int Age { get; set; }
        [XmlIgnore]//添加该特性后,在进行XML格式序列化时,Gender属性将会被忽略
        public string Gender { get; set; }
        public string SayHello()
        {
            return string.Format("大家好,我叫{0},年龄{1},性别{2}", Name, Age, Gender);
        }
    }
}

第二步,运行测试:点击运行,在打开的窗体上点击按钮【XML序列化】,然后打开指定盘符,打开 XML 文件person.xml。

【C#面向对象】第九课——C#中的序列化和反序列化_第22张图片

总结,其中用到的 XML 特性:

  • XmlRoot 定义根节点,将默认的“Person”修改为“个人信息”
  • XmlAttribute 定义 Xml 属性,原来 Name 是节点,现在改为“个人信息”节点的属性
  • XmlIgnore 忽略字段或属性,Gender 属性被忽略掉了

XML 序列化的特性还有很多,感兴趣的可以上网搜索相关资料。

 

4、序列化的作用

4.1  使用序列化保存集合

       在程序中我们经常会创建大量的对象,当这些对象需要保存时,我们可以将对象保存到集合中,再将集合进行序列化处理。下面我们将使用二进制和 XML 两种方式来保存 Person 类和两个子类 Student 类、Teacher 类的对象集合。

示例练习7:二进制序列化保存集合

第一步,新建窗体项目:打开Visual Studio软件后,单击【文件】——【新建】——【项目】,在新建项目对话框中选择【Window 窗体应用(.NET Framework)】,这里使用默认名称【WindowsFormsApp3】,然后单击【确定】。

第二步,添加Person类:在解决方案资源管理器中,右键单击项目名称【WindowsFormsApp3】,依次点击【添加】→【类】,并设置类名为Person.cs,编写Person类。

【C#面向对象】第九课——C#中的序列化和反序列化_第23张图片

namespace WindowsFormsApp3
{
    [Serializable]
    public class Person
    {
        public Person(string name, int age, string gender)
        {
            this.Name = name;
            this.Age = age;
            this.Gender = gender;
        }
        public Person() { }
        public string Name { get; set; }
        public int Age { get; set; }
        public string Gender { get; set; }
        public string SayHello()
        {
            return string.Format("大家好,我叫{0},年龄{1},性别{2}", Name, Age, Gender);
        }
    }
}

第三步,添加Student子类:方法同第二步

【C#面向对象】第九课——C#中的序列化和反序列化_第24张图片

namespace WindowsFormsApp3
{
    [Serializable]
    public class Student : Person
    {
        public Student(string name, int age, string gender) : base(name, age, gender)
        {
        }
        public Student() { }
    }
}

第四步,添加Teacher子类:方法同第二步

【C#面向对象】第九课——C#中的序列化和反序列化_第25张图片

namespace WindowsFormsApp3
{
    [Serializable]
    public class Teacher : Person
    {
        public Teacher(string name, int age, string gender)
            : base(name, age, gender)
        {
        }
        public Teacher() { }
    }
}

第五步,设计窗体Form1:添加两个Button按钮,分别设置他们的Name属性和Text属性。

第一个Button:Name属性为:btnSerialize ,Text属性为:二进制序列化集合

第二个Button:Name属性为:btnDeserialize ,Text属性为:二进制反序列化集合

【C#面向对象】第九课——C#中的序列化和反序列化_第26张图片

第六步,给按钮添加点击事件:分别双击两个按钮,进入各自的Click事件,编写代码。

【C#面向对象】第九课——C#中的序列化和反序列化_第27张图片

        private void btnSerialize_Click(object sender, EventArgs e)//二进制序列化集合
        {
            List list = new List();//声明一个List集合,集合的数据类型是Person
            Person person1 = new Person("李明", 30, "男");//声明一个Person类的对象
            Student person2 = new Student("王峰", 23, "男");//声明一个Student类的对象
            Teacher person3 = new Teacher("赵强", 22, "男");//声明一个Teacher类的对象
            list.Add(person1);//将对象添加到集合中
            list.Add(person2);
            list.Add(person3);
            FileStream fileStream = new FileStream(@"D:\shu\person.bin", FileMode.Create);//①创建文件流,需要引入命名空间System.IO;
            BinaryFormatter formatter = new BinaryFormatter();//②创建二进制格式化器,需要引入命名空间System.Runtime.Serialization.Formatters.Binary;
            formatter.Serialize(fileStream, list);//③序列化对象,将文件流fileStream中的list对象进行序列化
            fileStream.Close();//④关闭文件流fileStream
        }


        private void btnDeserialize_Click(object sender, EventArgs e)//二进制反序列化集合
        {
            FileStream fileStream = new FileStream(@"D:\shu\person.bin", FileMode.Open);//①创建文件流
            BinaryFormatter formatter = new BinaryFormatter();//②创建二进制格式化器
            List persons = (List)formatter.Deserialize(fileStream);//③反序列化,获得对象,并将对象存在集合里
            fileStream.Close();//④关闭文件流
            foreach (Person person in persons)//遍历输出
            {
                MessageBox.Show(person.SayHello());//消息框弹出
            }
        }

第七步,运行测试:点击运行按钮,在弹出的窗体中,先点击【二进制序列化集合】按钮,再点击【二进制反序列化集合】按钮,查下效果。

【C#面向对象】第九课——C#中的序列化和反序列化_第28张图片

案例总结:上面序列化集合的代码和序列化单个对象的代码基本一样,操作很简单,这是因为二进制序列化除了保存集合所有对象的数据外,还能保存它们的类型信息,所以不同子类的对象都可以添加到List类型的集合中进行保存。

案例延伸:XML序列化是否可以使用上述方法序列化保存集合?

错误原因:XML 序列化只保存对象的公开字段和属性,不保存类型信息,如果想直接保存包括不同子类对 象的集合,会发生错误。

解决方法:给集合添加[XmlArrayItem]特性,用以说明集合中可能包括的对象类型。

 

示例练习8:XML 序列化保存集合

第一步,添加PersonList类:在示例7 的基础上,继续完成。

【C#面向对象】第九课——C#中的序列化和反序列化_第29张图片

代码分析:PersonList 类的作用很简单,就是包含一个 List集合,该集合使用[XmlArrayItem] 特性列举了可能包含的对象类型。

namespace WindowsFormsApp3
{
    public class PersonList
    {
        [XmlArrayItem(typeof(Person)), XmlArrayItem(typeof(Student)), XmlArrayItem(typeof(Teacher))]//需要引入命名空间System.Xml.Serialization
        public List persons = new List();
    }
}

第二步,设计窗体Form1:继续添加两个Button按钮,分别设置他们的Name属性和Text属性。

第一个Button:Name属性为:btnSer,Text属性为:XML序列化集合

第二个Button:Name属性为:btnDeser ,Text属性为:XML反序列化集合

【C#面向对象】第九课——C#中的序列化和反序列化_第30张图片

第三步,给按钮添加点击事件:分别双击两个按钮,进入各自的Click事件,编写代码。

【C#面向对象】第九课——C#中的序列化和反序列化_第31张图片

代码分析:代码中没有直接保存 List集合,而是把集合保存到 PersonList 对象中再序列化保存, 反序列化也是先获得 PersonList 对象,再访问里面的集合。

        private void btnSer_Click(object sender, EventArgs e)//XML序列化集合
        {
            List persons = new List() {
                new Person("张三", 18, "男"),
                new Student("李四", 23, "女"),
                new Teacher("王五", 22, "男")
            };//声明一个集合Persons,并存三个对象
            FileStream fileStream = new FileStream(@"D:\shu\person.xml", FileMode.Create);//①创建文件流
            XmlSerializer serializer = new XmlSerializer(typeof(PersonList));//②创建XML格式序列化器,需要引入命名空间System.Xml.Serialization;
            PersonList list = new PersonList();//new一个PersonList类的对象
            list.persons = persons;//将集合保存到PersonList的persons属性中
            serializer.Serialize(fileStream, list);//③序列化对象
            fileStream.Close();//④关闭文件流
        }

        private void btnDeser_Click(object sender, EventArgs e)//XML反序列化集合
        {
            FileStream fileStream = new FileStream(@"D:\shu\person.xml", FileMode.Open);
            XmlSerializer serializer = new XmlSerializer(typeof(PersonList));
            //反序列化PersonList对象
            PersonList list = (PersonList)serializer.Deserialize(fileStream);
            fileStream.Close();
            foreach (Person person in list.persons)
            {
                MessageBox.Show(person.SayHello());
            }
        }

第四步,运行测试:点击运行按钮,在弹出的窗体中,先点击【XML序列化集合】按钮,再点击【XML反序列化集合】按钮,查下效果。

【C#面向对象】第九课——C#中的序列化和反序列化_第32张图片

 

 

 

本章总结

  • 序列化(Serialization)是将对象状态转换为可以存储或传输形式的过程。而反序列化 (Deserialization)是从文件或数据流中将数据还原成对象的过程。
  • 常用的序列化方法有:
    • 二进制格式序列化
    • XML 格式序列化
  • 二进制格式序列化的步骤是: 1)创建文件流  2)创建二进制格式化器  3)序列化对象 4)关闭文件流
  • XML 格式序列化的步骤是: 1)创建文件流  2)创建 XML 序列化器  3)序列化对象  4)关闭文件流
  • 可以使用特性对序列化的结果进行控制。
  •  二进制格式序列化可以保持一个对象所有的数据和类型信息。
  • XML 格式序列化仅序列化公共属性和字段,不保留类型信息。

 

 

 

 

============这里是结束分割线================

你可能感兴趣的:(C#面向对象,序列化,反序列化,二进制序列化,xml序列化,序列化保存集合)