• C#基础笔记——序列化(Serialize)和反序列化(NonSerialize)


    一、概述

    序列化是把对象转变成流。相反的过程就是反序列化。

    哪些场合用到这项技术呢?

    1. 把对象保存到本地,下次运行程序时恢复这个对象。

    2. 把对象传送到网络的另一台终端上,然后在此终端还原这个对象。

    3. 复制系统的粘帖板中,然后用快捷键Ctrl+V恢复这个对象。

    常用的序列化流有Binary(二进制流),XML,SOAP。

    二、序列化和反序列化使用事例:

    这里我们把序列化和反序列化以功能类的形式展现:

     public class Serializer
        {
            //将类型序列化为字符串
            public static string Serialize<T>(T t) where T : class
            { 
                using(MemoryStream stream=new MemoryStream())
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    formatter.Serialize(stream, t);
                    return System.Text.Encoding.UTF8.GetString(stream.ToArray());
                }
            }
    
            //将类型序列化为文件
            public static void SerializeToFile<T>(T t, string path, string fullName) where T : class
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                string fullPath = string.Format(@"{0}{1}", path, fullName);
                using (FileStream stream = new FileStream(fullPath,FileMode.OpenOrCreate))
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    formatter.Serialize(stream, t);
                    stream.Flush();
                }
            }
    
            //将类型序列化为文件
            public static void SerializeToFileByXml<T>(T t, string path, string fullName) where T : class
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                
                string fullPath = string.Format(@"{0}{1}", path, fullName);
    
                using (FileStream stream = new FileStream(fullPath, FileMode.OpenOrCreate))
                {
                    XmlSerializer formatter = new XmlSerializer(typeof(T));
                    formatter.Serialize(stream, t);
                    stream.Flush();
                }
            }
            //将字符串反序列化为类型
            public static TResult Deserialize<TResult>(string s) where TResult : class
            {
                byte[] bs = System.Text.Encoding.UTF8.GetBytes(s);
                using (MemoryStream stream = new MemoryStream(bs))
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    return formatter.Deserialize(stream) as TResult;
                }
            }
    
            //将文件反序列化为类型
            public static TResult DeserializeFromFile<TResult>(string path) where TResult : class
            {
                using (FileStream stream = new FileStream(path,FileMode.Open))
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    return formatter.Deserialize(stream) as TResult;
                }
            }
    
            //将xml文件反序列化为类型
            public static TResult DeserializeFromFileByXml<TResult>(string path) where TResult : class
            {
                using (FileStream stream = new FileStream(path, FileMode.Open))
                {
                    XmlSerializer formatter = new XmlSerializer(typeof(TResult)); ;
                    return formatter.Deserialize(stream) as TResult;
                }
            }
        }

    上面事例中的方法是以泛型方法实现的,其中附加了泛型约束,保证泛型安全。

    序列化功能类有了下面我们建一个Book对象,用它来测试我们的功能类。

        [Serializable]
        public class Book
        {
            [NonSerialized]
            private string _bookPwd;
            [field: NonSerialized]
            public event EventHandler NameChanged;
            private string _bookName;
            private string _bookID;
            public ArrayList alBookReader;
            public string _bookPrice;
    
            public Book()
            {
                alBookReader = new ArrayList();
            }
    
            public string BookName
            {
                get { return _bookName; }
                set
                {
                    if (NameChanged != null)
                    {
                        NameChanged(this, null);
                    }
                    _bookName = value;
                }
            }
    
            public void BookPwd(string pwd)
            {
                 _bookPwd=pwd;
            }
           
            public string BookID
            {
                get { return _bookID; }
                set { _bookID = value; }
            }
    
            public void SetBookPrice(string price)
            {
                _bookPrice = price;
            }
    
            [OnDeserializedAttribute]
            public void changeName(StreamingContext context)
            {
                this.BookName = "C#深入浅出";
            }
    
            public void Write()
            {
                Console.WriteLine("Book ID:" + BookID);
                Console.WriteLine("Book Name:" + BookName);
                Console.WriteLine("Book Password:" + _bookPwd);
                Console.WriteLine("Book Price:" + _bookPrice);
                Console.WriteLine("Book Reader:");
                for (int i = 0; i < alBookReader.Count; i++)
                {
                    Console.WriteLine(alBookReader[i]);
                }
            }
        }

    关键介绍:
    1.[Serializable]特性定义该类型可以被序列化;

    2.[NonSerialized]定义某个属性不被序列化,即:内部成员被NonSerialized禁止序列化特性标记;

    3.[field: NonSerialized]定义事件不被序列化;

    4.[OnDeserializedAttribute]当它应用于某个方法时,会指定对象被反序列化后立即执行此方法。

    5.[OnDeserializingAttribute]当它应用于某个方法时,会指定对象被反序列化时立即执行此方法。

    6.[OnSerializedAttribute]如果将对象图应用于某个方法时,会指定在序列化该对象图后是否调用此方法。

    7.[OnSerializingAttribute]当它应用于某个方法时,会指定在对象序列化前调用此方法。、

    我们用控制台程序实现序列化和反序列化:

    static void Main(string[] args)
            {
                string path = "c:\Test\";
                Book book = new Book();
                book.NameChanged += new EventHandler(make_NameChanged);
                book.BookID = "2001";
                book.alBookReader.Add("Abel");
                book.alBookReader.Add("Tomson");
                book.BookName = "敏捷无敌";
                book.BookPwd("*****");
                book.SetBookPrice("102.00");
                //对象序列化
                Serializer.SerializeToFileByXml<Book>(book, path, "book.txt");
                //对象反序列化
                Book anothorbookserialize = new Book();
                anothorbookserialize = Serializer.DeserializeFromFileByXml<Book>(path + "book.txt");
                anothorbookserialize.Write();
                Console.ReadKey();
            }
    
            static void make_NameChanged(object sender, EventArgs e)
            {
                Console.WriteLine("Name Changed");
            }

    我们的事例是调用XML序列化流文件形式,序列化执行后的文件如下:

    <?xml version="1.0"?>
    <Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <alBookReader>
        <anyType xsi:type="xsd:string">Abel</anyType>
        <anyType xsi:type="xsd:string">Tomson</anyType>
      </alBookReader>
      <_bookPrice>102.00</_bookPrice>
      <BookName>敏捷无敌</BookName>
      <BookID>2001</BookID>
    </Book>

    反序列化输出如下:

    Name Changed
    Book ID:2001
    Book Name:敏捷无敌
    Book Password:
    Book Price:102.00
    Book Reader:
    Abel
    Tomson

    结果分析:

    [NonSerialized]Book Password属性在序列化XML文件中没有出现Book Password

    [field: NonSerialized]NameChanged事件,在序列化XML文件中没有出现NameChanged。

    [OnDeserializedAttribute]changeName()方法,执行反序列化后没有立即执行changeName()方法,Book Name名称没有改变,需要通过Binary流形式才能成功执行方法。

        //序列化
        Serializer.SerializeToFile<Book>(book, path, "book.txt");
        //反序列化
        anothorbookserialize = Serializer.DeserializeFromFile<Book>(path + "book.txt");

    我们以XML流为例是为了更好的理解序列化和反序列化的执行过程,实际应用中多数以Binary流形式实现序列化和反序列化较多。

    三、继承ISerializable接口更灵活的控制序列化过程:

      当以上Serializable特性无法满足复杂的序列化过程时就需要实现ISerializable接口了。

      以下是格式化器的工作流程:如果格式化器在序列化一个对象的时候,发现对象实现了ISerializable接口,那他会忽略类所有序列化特性,转而调用GetObjectData方法的一个SerializationInfo对象,方法内部负责该对象属性的添加。反序列化时调用该对象保护参数构造方法中的一个SerializationInfo对象,方法内部对象属性赋值。

    下面我们实现ISerializable接口的子类型应负责父类型的序列化为例:

    1.父类同样实现了ISerializable接口

    [Serializable]
        public class Person:ISerializable
        {
            public string Name{get;set;}
            
            public Person()
            {
    
            }
    
            protected Person(SerializationInfo info,StreamingContext context)
            {
                Name = info.GetString("Name");
            }
    
            public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("Name",Name);
            }
        }
        [Serializable]
        public class Employee: Person , ISerializable
        {
            public int Salary{get;set;}
    
    public Employee() { } protected Employee(SerializationInfo info, StreamingContext context) { Salary = info.GetInt32("Salary"); Name = info.GetString("Name"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info,context); info.AddValue("Salary", Salary); } }

    注意:Employee中的GetObjectData方法覆盖了基类Person中的虚方法GetObjectData。

    Employee employee = new Employee() { Name = "Abel", Salary=1220 };
    BinarySerializer.SerializeToFile<Employee>(employee, strFile, "employee.txt");
    employee = BinarySerializer.DeserializeFromFile<Employee>("c:\Test\employee.txt");
    Console.WriteLine(employee.Name);
    Console.WriteLine(employee.Salary);

    2.若父类没有实现了ISerializable接口如何处理呢?

    我们要实现继承ISerializable接口的Employee类的一个父类Person,Person没有实现ISerializable接口,序列化器没有默认去处理Person对象,只能由我们自己去做。

    下面我们用具体实例实现:

       [Serializable]
        public class Person
        {
            public string Name{get;set;} }
    
        [Serializable]
        public class Employee: Person , ISerializable
        {
            public int Salary{get;set;}
    
            public Employee()
            {
    
            }
    
            protected Employee(SerializationInfo info, StreamingContext context)
            {
                Salary = info.GetInt32("Salary");
                Name = info.GetString("Name");
            }
    
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("Name", Name);
                info.AddValue("Salary", Salary);
            }
        }

    在这此序列化学习中我们用到了事件、泛型和流文件的处理知识,通过序列化我们可以实现本地加载,远程还原对象。

    得到了一个Serializer工具类,该工具类封装了序列化和反序列化的过程。

  • 相关阅读:
    mabatis的批量新增sql 初级的 初级的 初级的
    (批量更新)对多个符合条件的id做更新操作
    js中的 !! 和 ! 的区别
    js中===和==的区别
    select下拉框使用完毕后,重置按钮使其清空
    select下拉框的数据回显
    字符串拼串 能缓解我们的开发难度→!←(ε=(´ο`*)))唉,又是一个不知道该怎么写题目的随笔啊,头疼)
    select下拉框可以直接取list里的内容 不用非得转map (不得不承认我是个ZZ,这么简单的问题才反应过来,--^--)
    sql中某条件不为空,可能有的小祖宗会喷了,这还用总结?emmm,我渣,我觉得有一点意思对于第二种(土味)
    左查询left join on简单总结
  • 原文地址:https://www.cnblogs.com/Abel-Zhang/p/Serialize.html
Copyright © 2020-2023  润新知