• .NET序列化 serializable,反序列化


    web 请求服务器,是不能传递跟客户端那种{传递任意类型的数据【类】等},只能传递字符串、或字节流等,反正传递过去不能 .XXX成员这样方式的,序列化到服务器端,反序列化,服务器需要对应的类型的。

    序列化:将对象的状态信息及类型信息,转换为一种易于传输或存储形式(流,即字节序列)的过程
    反序列化:与序列化相反,将流转换为对象的过程。
    常用的有二进制序列化、XML序列化及JSON序列化三种序列化方式。现在一般都是json格式传输了。比一般格式更加轻量化了,更易于传输。效率更高
    序列化是指将对象实例的状态存储到存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字
    节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本
    NET自身提供了对二进制序列化与XML序列化的支持。我们可以借助第三方库,如Newtonsoft.Json【是个dll{动态链接库}】,来实现JSON序列化
    在这里插入图片描述

    按值封送
    对象仅在创建对象的应用程序域中有效。除非对象是从 MarshalByRefObject 派生得到或标记为 Serializable,否则,任何将对象作为参数传递或
    将其作为结果返回的尝试都将失败。如果对象标记为 Serializable,则该对象将被自动序列化,并从一个应用程序域传输至另一个应用程序域,然后
    进行反序列化,从而在第二个应用程序域中产生出该对象的一个精确副本。此过程通常称为按值封送。
    如果对象是从 MarshalByRefObject 派生得到,则从一个应用程序域传递至另一个应用程序域的是对象引用,而不是对象本身。也可以将从
    MarshalByRefObject 派生得到的对象标记为 Serializable。远程使用此对象时,负责进行序列化并已预先配置为 SurrogateSelector 的格式化程
    序将控制序列化过程,并用一个代理替换所有从 MarshalByRefObject 派生得到的对象。如果没有预先配置为 SurrogateSelector,序列化体系结
    构将遵从下面的标准序列化规则

    二进制序列化会将对象的所有属性(即使访问修饰符是private)转换到流中,XML/JSON则只转换访问修饰符为public的属性。在反序列化时,二进制形式不会调用构造函数,可以借助二进制序列化来实现深拷贝。
    XML/JSON序列化不受编程语言限制,C#使用XML/JSON序列化后的数据JAVA可以很容易的按照XML或JSON的格式反序列化得到所需数据。相对而言,二进制序列化则受到编程语言的限制、

    [Serializable]
    public class Person
    {
    	 public Person()
    	 {
    		 Console.WriteLine("ctor");
    	 }
     
    	 public Person(int gender)
    	 {
    		 _gender = gender;
    		 Console.WriteLine("ctor with parameter");
    	 }
     
    	 private static int TAG = 20;
    	 private int _gender;
     
    	 public string Name { set; get; }
    	 public int Age { set; get; }
    }
    // 创建对象  此处使用了初始化器
    var p = new Person(10)
    {
    	 Name = "xfh",
    	 Age = 26
    };
    // Json序列化,只会序列化public属性
    var pStr = JsonConvert.SerializeObject(p);
    Console.WriteLine(System.Text.Encoding.Default.GetByteCount(pStr));
    var newP2 = JsonConvert.DeserializeObject<Person>(pStr); 
    
    // **二进制序列化,**序列化所有属性及字段(即便是访问级别是private)
    var binaryFormatter = new BinaryFormatter();
    using (var stream = new MemoryStream())
    {
    	 binaryFormatter.Serialize(stream, p);
    	 Console.WriteLine(stream.Length);
    	 stream.Position = 0;
    	 // 反序列化不会调用构造函数
    	 var newP = (Person)binaryFormatter.Deserialize(stream);
    }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    // xml序列化,目标类型必须具有无参构造函数,只会序列化public属性
    var xmlFormatter = new XmlSerializer(typeof(Person));
    using (var stream = new MemoryStream())
    using (var fs = new FileStream(@“C:\Users\xfh\Desktop\stream.xml”, FileMode.OpenOrCreate))
    using (var sr = new StreamReader(stream))
    using (var sw = new StreamWriter(fs))
    {
    // 序列化
    xmlFormatter.Serialize(stream, p);
    stream.Position = 0;
    // 写入XML文件中
    while (sr.EndOfStream == false)
    {
    var content = sr.ReadLine();
    sw.WriteLine(content);
    }
    stream.Position = 0;
    // 反序列化
    var newP3 = (Person)xmlFormatter.Deserialize(stream);
    }
    流类型[memorystream、filestream、networkStream、stream等]可以方便地操作各种字节流,但是如何把现有的实例对象转换为方便传输的字节流,就需要使用序列化技术。对象实例的序列化,是指将实例对象转换为可方便存储、传输和交互的。在.NET中,通过Serializable特性提供了序列化对象实例的机制,当一个类型被申明为Serializable后,它就能被诸如BinaryFormatter等实现了IFormatter接口的类型进行序列化和反序列化。我们可以为某些成员添加NonSerialized特性,不希望被序列化的成员。

    class Program
        {
            static void Main(string[] args)
            {
                Person obj = new Person(26, "Edison Chou");
                Console.WriteLine("初始状态:");
                Console.WriteLine(obj);
    
                // 序列化对象
                byte[] data = Serialize(obj);
                // 反序列化对象
                Person newObj = DeSerialize(data);
    
                Console.WriteLine("经过序列化和反序列化后:");
                Console.WriteLine(newObj);
    
                Console.ReadKey();
            }
    
            // 序列化对象
            static byte[] Serialize(Person p)
            {
                // 使用二进制序列化
                IFormatter formatter = new BinaryFormatter();
                using (MemoryStream ms = new MemoryStream())
                {
                    formatter.Serialize(ms, p);
                    return ms.ToArray();
                }
            }
    
            // 反序列化对象
            static Person DeSerialize(byte[] data)
            {
                // 使用二进制反序列化
                IFormatter formatter = new BinaryFormatter();
                using (MemoryStream ms = new MemoryStream(data))
                {
                    Person p = formatter.Deserialize(ms) as Person;
                    return p;
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    在这里插入图片描述
    .NET中提供了三种对象序列格式化类型:BinaryFormatter、SoapFormatter和XmlSerializer

    (1)BinaryFormatter

    顾名思义,BinaryFormatter可用于将可序列化的对象序列化成二进制的字节流,在前面Serializable特性的代码示例中已经展示过,这里不再重复展示。

    (2)SoapFormatter

    SoapFormatter致力于将可序列化的类型序列化成符合SOAP规范的XML文档以供使用。在.NET中,要使用SoapFormatter需要先添加对于SoapFormatter的引用:

    using System.Runtime.Serialization.Formatters.Soap;
    Tips:SOAP是一种位于应用层的网络协议,它基于XML,并且是Web Service的基本协议。
    在这里插入图片描述
    [Serializable]
    public class Person
    {

    public Person()
    {
    }

    }
    ② XmlSerializer只能序列化公共成员变量;

    因此,Person类中的私有成员_number便不能被XmlSerializer进行序列化
     [Serializable]
    public class Person
    {
    // 私有成员无法被XmlSerializer序列化
    private int _number;
    }

    ****综合演示SoapFormatter和XmlSerializer的使用方法:

    ①重新改写Person类

    [Serializable]
    public class Person
    {
    // 私有成员无法被XmlSerializer序列化
    private int _number;
    // 使用NonSerialized特性标记此成员不可被BinaryFormatter和SoapFormatter序列化
    [NonSerialized]
    public string _name;
    // 使用XmlIgnore特性标记此成员不可悲XmlSerializer序列化
    [XmlIgnore]
    public string _univeristy;

        public Person()
        {
        }
    
        public Person(int i, string s, string u)
        {
            this._number = i;
            this._name = s;
            this._univeristy = u;
        }
    
        public override string ToString()
        {
            string result = string.Format("学号是:{0},姓名是:{1},大学是:{2}", _number, _name, _univeristy);
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    ②新增SoapFormatter和XmlSerializer的序列化和反序列化方法
    #region 01.SoapFormatter
    // 序列化对象-SoapFormatter
    static byte[] SoapFormatterSerialize(Person p)
    {
    // 使用Soap协议序列化
    IFormatter formatter = new SoapFormatter();
    using (MemoryStream ms = new MemoryStream())
    {
    formatter.Serialize(ms, p);
    return ms.ToArray();
    }
    }

    // 反序列化对象-SoapFormatter
    static Person SoapFormatterDeSerialize(byte[] data)
    {
        // 使用Soap协议反序列化
        IFormatter formatter = new SoapFormatter();
        using (MemoryStream ms = new MemoryStream(data))
        {
            Person p = formatter.Deserialize(ms) as Person;
            return p;
        }
    } 
    #endregion
    
    #region 02.XmlSerializer
    // 序列化对象-XmlSerializer
    static byte[] XmlSerializerSerialize(Person p)
    {
        // 使用XML规范序列化
        XmlSerializer serializer = new XmlSerializer(typeof(Person));
        using (MemoryStream ms = new MemoryStream())
        {
            serializer.Serialize(ms, p);
            return ms.ToArray();
        }
    }
    
    // 反序列化对象-XmlSerializer
    static Person XmlSerializerDeSerialize(byte[] data)
    {
        // 使用XML规范反序列化
        XmlSerializer serializer = new XmlSerializer(typeof(Person));
        using (MemoryStream ms = new MemoryStream(data))
        {
            Person p = serializer.Deserialize(ms) as Person;
            return p;
        }
    } 
    #endregion
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    自定义序列化和反序列化的过程

    在这里插入图片描述

    对于某些类型,序列化和反序列化往往有一些特殊的操作或逻辑检查需求,这时就需要我们能够主动地控制序列化和反序列化的过程。.NET中提供的Serializable特性帮助我们非常快捷地申明了一个可序列化的类型(因此也就缺乏了灵活性),但很多时候由于业务逻辑的要求,我们需要主动地控制序列化和反序列化的过程。因此,.NET提供了ISerializable接口来满足自定义序列化需求。

    下面的代码展示了自定义序列化和反序列化的类型模板:
    [Serializable]
    public class MyObject : ISerializable
    {
    protected MyObject(SerializationInfo info, StreamingContext context)
    {
    // 在此构造方法中实现反序列化
    }

        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            // 在此方法中实现序列化
        }
    }
     如上代码所示,GetObjectData和特殊构造方法都接收两个参数:SerializationInfo 类型参数的作用类似于一个哈希表,通过key/value对来存储整个对象的内容,而StreamingContext 类型参数则包含了流的当前状态,我们可以根据此参数来判断是否需要序列化和反序列化类型独享。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果基类实现了ISerializable接口,则派生类需要针对自己的成员实现反序列化构造方法,并且重写基类中的GetObjectData方法。

    下面通过一个具体的代码示例,来了解如何在.NET程序中自定义序列化和反序列化的过程:

    ①首先我们需要一个需要被序列化和反序列化的类型,该类型有可能被其他类型继承
    [Serializable]
    public class MyObject : ISerializable
    {
    private int _number;
    [NonSerialized]
    private string _name;

        public MyObject(int num, string name)
        {
            this._number = num;
            this._name = name;
        }
    
        public override string ToString()
        {
            return string.Format("整数是:{0}\r\n字符串是:{1}", _number, _name);
        }
    
        // 实现自定义的序列化
        protected MyObject(SerializationInfo info, StreamingContext context)
        {
            // 从SerializationInfo对象(类似于一个HashTable)中读取内容
            this._number = info.GetInt32("MyObjectInt");
            this._name = info.GetString("MyObjectString");
        }
    
        // 实现自定义的反序列化
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            // 将成员对象写入SerializationInfo对象中
            info.AddValue("MyObjectInt", this._number);
            info.AddValue("MyObjectString", this._name);
        }
    }
    
    ②随后编写一个继承自MyObject的子类,并添加一个私有的成员变量。需要注意的是:子类必须负责序列化和反序列化自己添加的成员变量。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    [Serializable]
    public class MyObjectSon : MyObject
    {
    // 自己添加的成员
    private string _sonName;

        public MyObjectSon(int num, string name)
            : base(num, name)
        {
            this._sonName = name;
        }
    
        public override string ToString()
        {
            return string.Format("{0}\r\n之类字符串是:{1}", base.ToString(), this._sonName);
        }
    
        // 实现自定义反序列化,只负责子类添加的成员
        protected MyObjectSon(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            this._sonName = info.GetString("MyObjectSonString");
        }
    
        // 实现自定义序列化,只负责子类添加的成员
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("MyObjectSonString", this._sonName);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    ③最后编写Main方法,测试自定义的序列化和反序列化
    class Program
    {
    static void Main(string[] args)
    {
    MyObjectSon obj = new MyObjectSon(10086, “Edison Chou”);
    Console.WriteLine(“初始对象为:”);
    Console.WriteLine(obj.ToString());
    // 序列化
    byte[] data = Serialize(obj);
    Console.WriteLine(“经过序列化与反序列化之后:”);
    Console.WriteLine(DeSerialize(data));

            Console.ReadKey();
        }
    
        // 序列化对象-BinaryFormatter
        static byte[] Serialize(MyObject p)
        {
            // 使用二进制序列化
            IFormatter formatter = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                formatter.Serialize(ms, p);
                return ms.ToArray();
            }
        }
    
        // 反序列化对象-BinaryFormatter
        static MyObject DeSerialize(byte[] data)
        {
            // 使用二进制反序列化
            IFormatter formatter = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream(data))
            {
                MyObject p = formatter.Deserialize(ms) as MyObject;
                return p;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述

  • 相关阅读:
    DCDC电源模块 HRA(B) W1~25W系列 隔离宽电压输入 高电压稳压输出
    基于51单片机的停车场管理系统仿真电路设计
    【MySQL】存储过程与存储函数
    开源博客项目Blog .NET Core源码学习(23:App.Hosting项目结构分析-11)
    产品生命周期有哪些
    基于session实现发送短信和验证码登录注册功能
    如何加载带有 AM、PM 的时间类型数据
    前端面试题整理(一)
    使用VueCli快速搭建项目
    webstorm HbuilderX工具未配置
  • 原文地址:https://blog.csdn.net/u013400314/article/details/126666833