当前位置: 首页 > 编程笔记 >

浅谈.Net中的序列化和反序列化

公良修竹
2023-03-14
本文向大家介绍浅谈.Net中的序列化和反序列化,包括了浅谈.Net中的序列化和反序列化的使用技巧和注意事项,需要的朋友参考一下

序列化和反序列化相信大家都经常听到,也都会用, 然而有些人可能不知道:.net为什么要有这个东西以及.net Frameword如何为我们实现这样的机制, 在这里我也是简单谈谈我对序列化和反序列化的一些理解。

一、什么序列化和反序列化

序列化通俗地讲就是将一个对象转换成一个字节流的过程,这样就可以轻松保存在磁盘文件或数据库中。反序列化是序列化的逆过程,就是将一个字节流转换回原来的对象的过程。

然而为什么需要序列化和反序列化这样的机制呢?这个问题也就涉及到序列化和反序列化的用途了,

对于序列化的主要用途有:

  • 将应用程序的状态保存在一个磁盘文件或数据库中,并在应用程序下次运行时恢复状态。例如, Asp.net 中利用序列化和反序列化来保存和恢复会话状态。
  • 一组对象可以轻松复制到Windows 窗体的剪贴板中,再粘贴回同一个或者另一个应用程序。
  • 将对象按值从一个应用程序域中发送到另一个程序域

并且如果把对象序列化成内存中的字节流,就可以利用一些其他的技术来处理数据,例如,对数据进行加密和压缩等。

二、序列化和反序列简单使用

.Net Framework 提供二种序列化方式:

  • 二进制序列化
  • XML 和SOAP序列化

序列化和反序列化的简单使用:

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace Serializable
{
  [Serializable]
  public class Person
  {
    public string personName;

    [NonSerialized]
    public string personHeight;

    private int personAge;
    public int PersonAge
    {
      get { return personAge; }
      set { personAge = value; }
    }

    public void Write()
    {
      Console.WriteLine("Person Name: "+personName);
      Console.WriteLine("Person Height: " +personHeight);
      Console.WriteLine("Person Age: "+ personAge);
    }

  }
  class Program
  {
    static void Main(string[] args)
    {
      Person person = new Person();
      person.personName = "Jerry";
      person.personHeight = "175CM";
      person.PersonAge = 22;
      Stream stream = Serialize(person);

      //为了演示,都重置
      stream.Position = 0;
      person = null;

      person = Deserialize(stream);
      person.Write();
      Console.Read();

    }
    private static MemoryStream Serialize(Person person)
    {
      MemoryStream stream = new MemoryStream();

      // 构造二进制序列化格式器
      BinaryFormatter binaryFormatter = new BinaryFormatter();
      // 告诉序列化器将对象序列化到一个流中
      binaryFormatter.Serialize(stream, person);

      return stream;

    }

    private static Person Deserialize(Stream stream)
    {
      BinaryFormatter binaryFormatter = new BinaryFormatter();
      return (Person)binaryFormatter.Deserialize(stream);
    }

  }
}

主要是调用System.Runtime.Serialization.Formatters.Binary命名空间下的BinnaryFormatter类来进行序列化和反序列化,调用反序列化后的结果截图:

从中可以看出除了标记NonSerialized的其他成员都能序列化,注意这个属性只能应用于一个类型中的字段,而且会被派生类型继承。

SOAP 和XML 的序列化和反序列化和上面类似,只需要改下格式化器就可以了, 这里我就不列出来了。

三、控制序列化和反序列化

有两种方式来实现控制序列化和反序列化:

  • 通过OnSerializing, OnSerialized,OnDeserializing, OnDeserialized,NonSerialized和OptionalField等属性
  • 实现System.Runtime.Serialization.ISerializable接口

第一种方式实现控制序列化和反序列化代码:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace ControlSerialization
{
  [Serializable]
  public class Circle
  {
    private double radius; //半径
    [NonSerialized]
    public double area; //面积

    public Circle(double inputradiu)
    {
      radius = inputradiu;
      area = Math.PI * radius * radius;
    }

    [OnDeserialized]
    private void OnDeserialized(StreamingContext context)
    {
      area = Math.PI * radius * radius;
    }

    public void Write()
    {
      Console.WriteLine("Radius is: " + radius);
      Console.WriteLine("Area is: " + area);
    }
  }
  class Program
  {

    static void Main(string[] args)
    {
      Circle c = new Circle(10);
      MemoryStream stream =new MemoryStream();
      BinaryFormatter formatter = new BinaryFormatter();
      // 将对象序列化到内存流中,这里可以使用System.IO.Stream抽象类中派生的任何类型的一个对象, 这里我使用了 MemoryStream类型。
      formatter.Serialize(stream,c);
      stream.Position = 0;
      c = null;
      c = (Circle)formatter.Deserialize(stream);
      c.Write();
      Console.Read();

    }
  }
}

运行结果为:

注意:如果注释掉 OnDeserialized属性的话,area字段的值就是0了,因为area字段没有被序列化到流中。

在上面需要序列化的对象中,格式化器只会序列化对象的radius字段的值。area字段中的值不会序列化,因为该字段已经应用了NonSerializedAttribute属性,然后我们用Circle c=new Circle(10)这样代码构建一个Circle对象时,在内部,area会设置一个约为314.159这样的值,这个对象序列化时,只有radius的字段的值(10)写入流中, 但当反序列化成一个Circle对象时,它的area字段的值会初始化为0,而不是约314.159的一个值。为了解决这样的问题,所以自定义一个方法应用OnDeserializedAttribute属性。此时的执行过程为:每次反序列化类型的一个实例,格式化器都会检查类型中是否定义了 一个应用了该attribute的方法,如果是,就调用该方法,调用该方法时,所有可序列化的字段都会被正确设置。除了OnDeserializedAttribute这个定制attribute,system.Runtime.Serialization命名空间还定义了OnSerializingAttribute,OnSerializedAttribute和OnDeserializingAttribute这些定制属性。

实现ISerializable接口方式控制序列化和反序列化代码:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;

namespace ControlSerilization2
{
  [Serializable]
  public class MyObject : ISerializable
  {
    public int n1;
    public intn2;

    [NonSerialized]
    public String str;

    public MyObject()
    {
    }

    protected MyObject(SerializationInfo info, StreamingContext context)
    {
      n1 = info.GetInt32("i");
      n2 = info.GetInt32("j");
      str = info.GetString("k");
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
      info.AddValue("i", n1);
      info.AddValue("j", n2);
      info.AddValue("k", str);
    }

    public void Write()
    {
      Console.WriteLine("n1 is: " + n1);
      Console.WriteLine("n2 is: " + n2);
      Console.WriteLine("str is: " + str);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      MyObject obj = new MyObject();
      obj.n1 = 2;
      obj.n2 = 3;
      obj.str = "Jeffy";
      MemoryStream stream = new MemoryStream();
      BinaryFormatter formatter = new BinaryFormatter();
      // 将对象序列化到内存流中,这里可以使用System.IO.Stream抽象类中派生的任何类型的一个对象, 这里我使用了 MemoryStream类型。
      formatter.Serialize(stream, obj);
      stream.Position = 0; 
      obj = null;
      obj = (MyObject)formatter.Deserialize(stream);
      obj.Write();
      Console.Read();
    }
  }
}

结果为:

此时的执行过程为:当格式化器序列化对象时,会检查每个对象,如果发现一个对象的类型实现了ISerializable接口,格式化器会忽视所有定制属性,改为构造一个新的System.Runtime.Serialization.SerializationInfo对象,这个对象包含了要实际为对象序列化的值的集合。构造好并初始化好SerializationInfo对象后,格式化器调用类型的GetObjectData方法,并向它传递对SerializationInfo对象的引用,GetObjectData方法负责决定需要哪些信息来序列化对象,并将这些信息添加到SerializationInfo对象中,通过调用AddValue方法来添加需要的每个数据,添加好所有必要的序列化信息后,会返回至格式化器,然后格式化器获取已经添加到SerializationInfo对象中的所有值,并将它们都序列化到流中,当反序列化时,格式化器从流中提取一个对象时,会为新对象分配内存,最初,这个对象的所有字段都设为0或null,然后,格式化器检查类型是否实现了ISerializable接口,如果存在这个接口, 格式化器就尝试调用一个特殊构造器,它的参数和GetObjectData方法的完全一致。

四、格式化器如何序列化和反序列化

从上面的分析中可以看出,进行序列化和反序列化主要是格式化器在工作的,然而下面就是要讲讲格式化器是如何序列化一个应用了 SerializableAttribute 属性的对象。

  1. 格式化器调用FormatterServices的GetSerializableMembers方法:public static MemberInfo[] GetSerializableMembers(Type type,StreamingContext context);这个方法利用发射获取类型的public和private实现字段(标记了NonSerializedAttributee属性的字段除外)。方法返回由MemberInfo对象构成的一个数组,其中每个元素对应于一个可序列化的实例字段。
  2. 对象被序列化,System.Reflection.MemberInfo对象数组传给FormatterServices的静态方法GetObjectData: public static object[] GetObjectData(Object obj,MemberInfo[] members); 这个方法返回一个Object数组,其中每个元素都标识了被序列化的那个对象中的一个字段的值。
  3. 格式化器将程序集标识和类型的完整名称写入流中。
  4. 格式化器然后遍历两个数组中的元素,将每个成员的名称和值写入流中。

接下来是解释格式化器如何自动反序列化一个应用了 SerializableAttribute属性的对象。

  1. 格式化器从流中读取程序集标识和完整类型名称。
  2. 格式化器调用FormatterServices的静态方法GetUninitializedObject: public static Object GetUninitializedObject(Type ttype);这个方法为一个新对象分配内存,但不为对象调用构造器。然而,对象的所有字段都被初始化为0或null.
  3. 格式化器现在构造并初始化一个MemberInfo数组,调用FormatterServices的GetSerializableMembers方法,这个方法返回序列化好、现在需要反序列化的一组字段。
  4. 格式化器根据流中包含的数据创建并初始化一个Object数组。
  5. 将对新分配的对象、MemberInfo数组以及并行Object数组的引用传给FormatterServices的静态方法PopulateObjectMembers:

public static Object PopulateObjectMembers(Object obj,MemberInfo[] members,Object[] data);这个方法遍历数组,将每个字段初始化成对应的值。

注:格式化如何序列化和反序列对象部分摘自CLR via C#(第三版),写在这里可以让初学者进一步理解格式化器在序列化和反序列化过程中所做的工作。

写到这里这篇关于序列化和反序列的文章终于结束了, 希望对自己以后复习和园子里的朋友有帮助。

以上就是浅谈.Net中的序列化和反序列化的详细内容,更多关于.net 序列化和反序列化的资料请关注小牛知识库其它相关文章!

 类似资料:
  • 本文向大家介绍.Net中的序列化和反序列化详解,包括了.Net中的序列化和反序列化详解的使用技巧和注意事项,需要的朋友参考一下 序列化和反序列化相信大家都经常听到,也都会用, 然而有些人可能不知道:.net为什么要有这个东西以及.net Frameword如何为我们实现这样的机制, 在这里我也是简单谈谈我对序列化和反序列化的一些理解。 一、什么序列化和反序列化 序列化通俗地讲就是将一个对象转换成一

  • 本文向大家介绍.net的序列化与反序列化实例,包括了.net的序列化与反序列化实例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了.net的序列化与反序列化的实现方法。分享给大家供大家参考。具体方法如下: 1.序列化与反序列化概述 C#中如果需要:将一个结构很复杂的类的对象存储起来,或者通过网路传输到远程的客户端程序中去,这时就需要用到序列化,反序列化(Serialization & De

  • 本文向大家介绍.NET中JSON的序列化和反序列化的几种方式,包括了.NET中JSON的序列化和反序列化的几种方式的使用技巧和注意事项,需要的朋友参考一下 一、什么是JSON JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立 于编程语言的文本格式来存

  • 问题内容: 我注意到存储在Redis中的某些序列化对象在反序列化方面遇到问题。 当我对Redis中存储的对象类进行更改时,通常会发生这种情况。 我想了解问题,以便为解决方案设计一个清晰的方案。 我的问题是,什么导致反序列化问题?移除公共/私人财产会引起问题吗?也许添加新属性?向类添加新功能会产生问题吗?那么更多的构造函数呢? 在我的序列化对象中,我有一个属性Map,如果我更改(更新了一些属性,添加

  • 问题内容: Golang中将结构序列化和反序列化为字符串的最佳方法(完整性和性能)是什么,反之亦然? 例如,如果我有这个结构: 我想将其存储在Redis上并取回。我试过保存,整型和字符串,这很好,但是如何存储结构对象? 问题答案: 使用gob和base64可以解决问题,例如: 当您需要序列化自定义结构或类型(例如struct)时,只需添加以下行:

  • 主要内容:1 Java序列化和反序列化,2 Java序列化的优点,3 java.io.Serializable接口,4 Java ObjectOutputStream,5 Java ObjectInputStream,6 Java序列化的例子,7 Java反序列化的例子1 Java序列化和反序列化 Java中的序列化是一种将对象状态写入字节流的机制。它主要用于Hibernate,RMI,JPA,EJB和JMS技术。 序列化的反向操作称为反序列化,其中字节流被转换为对象。序列化和反序列化过程与平台