您的位置:首页 > 编程语言 > C#

c#序列化(Serialize)、反序列化(Deserialize)

2010-10-22 14:43 393 查看
序列化(Serialize)、反序列化(Deserialize)
序列化概念:(MSDN
为什么要使用序列化?两个最重要的原因是将对象状态保存到存储媒体,以便可以在以后阶段重新创建精确副本;以及将对象按值从一个应用程序域发送至另一个应用程序域。例如,序列化用于在 ASP.NET 中保存会话状态,并将对象复制到 Windows 窗体的剪贴板中。它还可用于在远程处理中将对象按值从一个应用程序域传递至另一个应用程序域。
白话理解:
就是将一个。Net的对象存储在磁盘或者其他东西里面,下次用的时候可以完好的再转化成一个。Net对象。(这个场景类似于通信中的传输数据。)
要保证存储在磁盘或其他东西里面数据可以完整正确的取出来,就需要对数据进行处理。这个处理就是序列化。

说明:
永久存储:
经常有必要将对象的字段值存储至磁盘,以后再检索此数据。尽管不依赖序列化也能很容易地实现这一点,但方法通常麻烦而容易出错,并且需要跟踪对象的层次结构时会逐渐变得更加复杂。假设要编写一个包含数千个对象的大型商务应用程序,并且必须为每个对象编写代码,以便将字段和属性保存至磁盘以及从磁盘进行还原。序列化为实现这一目标提供了方便的机制。
公共语言运行库可管理对象在内存中存储的方式,并通过使用反射提供一种自动序列化机制。当序列化对象时,类的名称、程序集和类实例的所有数据成员被写入存储区。对象经常以成员变量方式将引用存储至其他实例。当序列化类时,序列化引擎跟踪被引用的对象(已序列化),以确保同一对象不会被多次序列化。随 .NET Framework 一起提供的序列化体系结构可自动正确地处理对象图和循环引用。对于对象图的唯一要求是,也必须将已序列化的对象引用的所有对象标记为 Serializable。如果未进行标记,序列化程序尝试序列化未标记的对象时将引发异常。
当反序列化已序列化的类时,将重新创建该类,并且将自动还原所有数据成员的值。
按值封送:
对象仅在创建它们的应用程序域中有效。除非对象从 MarshalByRefObject 派生或者标记为 Serializable,否则尝试将对象作为参数传递或者作为结果返回时都将失败。如果将对象标记为 Serializable,则会自动序列化该对象,将该对象从一个应用程序域传输至另一个应用程序域,然后反序列化,以便在第二个应用程序域中生成该对象的一个精确副本。此过程通常称为按值封送。
如果对象从 MarshalByRefObject 派生,则会将对象引用(而不是对象本身)从一个应用程序域传递至另一个应用程序域。您还可以将从 MarshalByRefObject 派生的对象标记为 Serializable。此对象用于远程处理时,已使用代理项选择器 (SurrogateSelector) 预配置负责序列化的格式化程序,该格式化程序控制序列化过程,并用代理替换从 MarshalByRefObject 派生的所有对象。如果本地没有 SurrogateSelector,则序列化体系结构遵循序列化过程中的步骤中所述的标准序列化规则。

序列化的三种方法:
一:二进制序列化(BinarySerializable
首先定义一个要序列化的类
[Serializable]
public class Class1
{
string _str;
[NonSerialized]
string _str2;
public string Str
{
get { return _str; }
set { _str = value; }
}
public string Str2
{
get { return _str2; }
set { _str2 = value; }
}
}
在类的声明前加上[Serializable]表明这个类可以被执行序列化。
在某个成员前面加上[NonSerialized]表示这个成员将不会被执行序列化。
下面我们对这个类执行序列化,将其内容存储在硬盘上,代码如下:
using。。。;
using System.IO;using System.Runtime.Serialization.Formatters.Binary;
。。。
public void BinarySerializable()
{
Class1 c = new Class1();
c.Str = "abc";
c.Str2 = "123";
FileStream fileStream = new FileStream("c://temp.dat", FileMode.Create);
BinaryFormatter b = new BinaryFormatter();
b.Serialize(fileStream, c);
fileStream.Close();
}
在以上代码执行后可以去查看本地磁盘,会存在一个二进制文件c://temp.dat,里面用二进制文件存储着这个对象的信息。
我们需要知道存储的到底对不对,需要反序列化,就要执行以下代码了:
public static void DeBinarySerializeNow()
{
Class1 c = new Class1();
FileStream fileStream = new FileStream("c://temp.dat", FileMode.Open, FileAccess.Read, FileShare.Read);
BinaryFormatter b = new BinaryFormatter();
c = b.Deserialize(fileStream) as Class1;
Console.WriteLine(c.Str);
Console.WriteLine(c.Str2);
fileStream.Close();
}
输出为:
abc

在本该输出123的地方输出的是一个空行.说明_str2 没有被序列化。
需要特别注意的是,在反序列化一个对象时不调用构造函数。
Class1 c = new Class1(); 这句代码说明最后反序列化生成的对象是由用户构造出来的,而不是有反序列化的时候构造出来的,反序列化只会将其被序列化的那些值赋给相对应的成员变量。

注意:序列化是不可以继承的
例如:
public class Class2:Class1
{
Public string _str3;
}
如果对class2执行序列化就会得到一个System.Runtime.Serialization.SerializationException异常:程序集“project123, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”中的类型“project123.Class2”未标记为可序列化。

二:Soap序列化(SoapSerializable
SOAP:简单对象访问协议,简单对象访问协议(SOAP)是一种轻量的、简单的、基于 XML 的协议,它被设计成在 WEB 上交换结构化的和固化的信息。 SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议( HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。
白话解释:
SoaP是一个规则,一个信息封装的规则,在web上交换信息就必须使用这样的规则来封装你的数据,别人才能识别解析,而不用大家互相沟通,不用告诉他我用什么方式封装我的数据信息。
Soap数据封装的基本结构如下:
<?xml version="1.0"?>
  <soap:Envelope
  xmlns:soap="http://www./2001/12/soap-envelope"
  soap:encodingStyle="http://www./2001/12/soap-encoding">
  <soap:Header>
  ...
  </soap:Header>
  <soap:Body>
  ...
  <soap:Fault>
  ...
  </soap:Fault>
  </soap:Body>
</soap:Envelope>
依旧使用上面的那个类
序列化:(此处的物理文件必须命名为xml格式,因为Soap序列化会生成xml格式的文本)
using。。。;
using System.IO;using System.Runtime.Serialization.Formatters. Soap;
注:System.Runtime.Serialization.Formatters. Soap名字空间需引用System.Runtime.Serialization.Formatters. Soap.dll

public void SoapSerializable()
{
Class1 c = new Class1();
c.Str = "abc";
c.Str2 = "123";
FileStream fileStream = new FileStream("c://temp.xml", FileMode.Create);
SoapFormatter b = new SoapFormatter();
b.Serialize(fileStream, c);
fileStream.Close();
}
反序列化:
public void DeSoapSerializeNow()
{
Class1 c = new Class1();
FileStream fileStream = new FileStream("c://temp.xml", FileMode.Open, FileAccess.Read, FileShare.Read);
SoapFormatter b = new SoapFormatter();
c = b.Deserialize(fileStream) as Class1;
Console.WriteLine(c.Str);
Console.WriteLine(c.Str2);
fileStream.Close();
}
序列化生成的文件中数据段位如下表示:
<a1:Class1 id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/project123/project123%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<_str id="ref-3">abc</_str>
</a1:Class1>

三:XML序列化(XMLSerialize
待序列化的类:
说明:不需要在类前面添加[Serializable]也不需要在成员前添加[NonSerialized]。
添加了也会被忽略掉。对于XMLSerialize来说,他只会序列化公开成员,如果那个公开成员不想被序列化需要在其前面添加 [XmlIgnore]标签。
(注:XMLSerialize还会序列化公开的属性如:public string Str{Get;set; })
在类前需要添加System.Xml.Serialization命名空间
public Class1
{
public Class1(string str1,string str2)
{
this._str = str1;
this._str2 = str2;
}
public Class1()
{
}
[XmlIgnore]
public string _str;
public string _str2;

public string Str
{
get { return _str; }
set { _str = value; }
}
public string Str2
{
get { return _str2; }
set { _str2 = value; }
}
}
序列化:
public void XMLSerialize()
{
Class1 c = new Class1("Str","Str2");
XmlSerializer xs = new XmlSerializer(typeof(Class1));
Stream stream = new FileStream("c://temp.xml ", FileMode.Create, FileAccess.Write, FileShare.Read);
xs.Serialize(stream, c);
stream.Close();
}
反序列化:
public static void XMLDeserialize()
{
XmlSerializer xs = new XmlSerializer(typeof(Class1));
Stream stream = new FileStream("c://temp.xml", FileMode.Open, FileAccess.Read, FileShare.Read);
Class1 c = xs.Deserialize(stream) as Class1;
Console.WriteLine(c.Str);
Console.WriteLine(c.Str2);
stream.Close();
}
问题来了:
1:按照以前的两个序列化方式,应该只会序列化_str2,可是在生成的的xml中数据如下:
<_str2>Str2</_str2>
<Str>Str</Str>
<Str2>Str2</Str2>
这说明他序列化了公开属性。
2:我们将_str2改为private的:private string _str2;
重新执行序列化,结果如下:
<Str>Str</Str>
<Str2>Str2</Str2>
说明他只会序列化公开的成员,而不会序列化私有的成员。
3:我们将class1中的无参构造去掉:
//public Class1()
//{
//}
执行序列化:
会在执行 XmlSerializer xs = new XmlSerializer(typeof(Class1));是抛出一个异常:project123.Class1 无法序列化,因为它没有无参数的构造函数。
说明需要执行xml序列化的类必须有一个无参构造函数。不然无法序列化。

序列化总结:
1: SoapSerializable将类信息序列化到xml文件中,序列化的信息包含:未标注为NonSerialized的成员变量
2: BinarySerializable将类信息序列化到二进制文件中,序列化的信息包括:未标注为NonSerialized的成员变量
3: XmlSerializer将类信息序列化到xml文件中,序列化的信息包括:未标注为XmlIgnore的public成员变量,以及公开的属性{get;set;}。
四:自定义序列化
1:自定义序列化首先设置要序列化的类实现ISerializable接口。
实现ISerializable接口就是实现void GetObjectData(SerializationInfo info, StreamingContext ctxt)方法
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("_str", _str);
info.AddValue("_str2",_str2);
}
SerializationInfo..::.AddValue 方法:
SerializationInfo..::.AddValue的第一个参数(string)是指这个数据在序列化后的文件中叫什么名字,可以叫任何名字,但是不要重复。
第二个参数就是我们要序列化的成员了(不管是私有的还是公开的),而且这个成员还可以添加操作后再赋值(例如:info.AddValue("_str", _str+“test”);)。

2:如果实现了ISerializable接口,则会忽略以前的 [XmlIgnore][NonSerialized]等标示符。
3:如果执行了自定义序列化,那么反序列化的时候也需要用户显式的定义一个私有构造函数,用以处理反序列化时,值的处理问题。
private Class1(SerializationInfo info, StreamingContext ctxt)
{
_str = (String)info.GetValue("_str", typeof(string));
_str2 = (String)info.GetValue("_str2", typeof(string));
}
如果info.AddValue("_str", _str+“test”);这个操作在序列化是进行过:
就需要在反序列化时指定对应的处理,如下:
_str = (String)info.GetValue("_str", typeof(string)).Trim("test".ToCharArray());(去掉尾部添加的test字符)
2:执行序列化。
执行序列化可以使用二进制序列化或者Soap序列化,但不能使用xml序列化(原因待考)。
3:序列化加密:
我们有如下加密类:
public static class Cryptographer
{
/// <summary>
/// 功能:加密方法,如果原文为Null或空字符串,结果同原文一样
/// </summary>
/// <param name="oragial">原文</param>
/// <returns>密文</returns>
public static string Encrypt(string oragial)
{

}
/// <summary>
///功能: 解密方法,如果密文为Null或空字符串,结果同密文一样
/// </summary>
/// <param name="encrypted">密文</param>
/// <returns>原文</returns>
public static string Decrypt(string encrypted)
{

}
}
首先,我们来序列化
以下是我们要序列化的类:
[Serializable]
public class Class1 : ISerializable
{
Cryptographer cry = new Cryptographer();
public Class1(string str1,string str2)
{
this._str = str1;
this._str2 = str2;
}
private Class1(SerializationInfo info, StreamingContext ctxt)
{
_str = Cryptographer.Decrypt((String)info.GetValue("_str", typeof(string))).Trim("test".ToCharArray());
_str2 = Cryptographer.Decrypt((String)info.GetValue("_str2", typeof(string)));
}
private string _str;
private string _str2;
public string Str
{
get { return _str; }
set { _str = value; }
}
public string Str2
{
get { return _str2; }
set { _str2 = value; }
}
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("_str", Cryptographer.Encrypt(_str));
info.AddValue("_str2",Cryptographer.Encrypt(_str2));
}
}
使用Soap序列化得到以下结果:
<_str id="ref-3">BvMOi+DwAYM=</_str>
<_str2 id="ref-4">bU1XWN+rQ/Y=</_str2>
更改类中一下内容
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("测试参数一", cry.Encrypt(_str));
info.AddValue("测试参数二", cry.Encrypt(_str2));
}
使用Soap序列化得到以下结果:
<测试参数一 id="ref-3">BvMOi+DwAYM=</测试参数一>
<测试参数二 id="ref-4">bU1XWN+rQ/Y=</测试参数二>
说明参数在序列化存储中的名称是可以任意设定的。存储的数据是可以进行任何可逆的操作的(要求可逆的原因是一会我们还要反序列化数据,要是不可逆数据就丢失了)。
同样我们使用二进制序列化得到以下内容:
project123.Class1[1] 娴嬭瘯鍙傛暟涓€娴嬭瘯鍙傛暟浜?[1]  BvMOi+DwAYM= bU1XWN+rQ/Y=

更换命名,命名为英文:
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("test1", cry.Encrypt(_str));
info.AddValue("test2", cry.Encrypt(_str2));
}
private Class1(SerializationInfo info, StreamingContext ctxt)
{
_str = cry.Decrypt((String)info.GetValue("test1", typeof(string)));
_str2 = cry.Decrypt((String)info.GetValue("test2", typeof(string)));
}
得到的结果如下:
project123.Class1[1] test1test2[1]  BvMOi+DwAYM= bU1XWN+rQ/Y=

可以看到存储了加密后的内容。

反序列化
首先执行二进制反序列化:
使用二进制反序列化:
public static void DeBinarySerializeNow()
{
Class1 c = new Class1();
FileStream fileStream = new FileStream("c://temp.dat", FileMode.Open, FileAccess.Read, FileShare.Read);
BinaryFormatter b = new BinaryFormatter();
c = b.Deserialize(fileStream) as Class1;
Console.WriteLine(c.Str);
Console.WriteLine(c.Str2);
fileStream.Close();
}
得到结果为:
abc
123
使用Soap执行反序列化:
public void DeSoapSerializeNow()
{
Class1 c = new Class1();
FileStream fileStream = new FileStream("c://temp.xml", FileMode.Open, FileAccess.Read, FileShare.Read);
SoapFormatter b = new SoapFormatter();
c = b.Deserialize(fileStream) as Class1;
Console.WriteLine(c.Str);
Console.WriteLine(c.Str2);
fileStream.Close();
}
得到结果为:
abc
123
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: