一 什么是序列化与反序列化
1-概念
把对象转换为字节序列的过程称为对象的序列化;
把字节序列恢复为对象的过程称为对象的反序列化。
序列化是将对象状态转换为可保持或传输的格式的过程,在序列化过程中,对象的公共字段和私有字段以及类的名称(包括包含该类的程序集)都被转换为字节流,然后写入数据流。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。
2-目的
其目的是以某种存储形式使自定义对象持久化(文本保存),或者将这种对象从一个地方传输到另一个地方(网络传输)。.
二 为什么要使用序列化
1-使用序列化的原因
a.一个原因是将对象的状态保持在存储媒体中,以便可以在以后重新创建精确的副本。
我们经常需要将对象的字段值保存到磁盘中,并在以后检索此数据。尽管不使用序列化也能完成这项工作,但这种方法通常很繁琐而且容易出错,并且在需要跟踪对象的层次结构时,会变得越来越复杂(当类型对象内部引用多个类型,存在嵌套很深的时候,逐层读取会很繁琐)。可以想象一下编写包含大量对象的大型业务应用程序的情形,程序员不得不为每一个对象编写代码,以便将字段和属性保存至磁盘以及从磁盘还原这些字段和属性。
序列化提供了轻松实现这个目标的快捷方法,对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的"深复制",即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。
个人经验:例如本地开发winFrom界面,实现控件界面记忆的功能,可以先定义一个实体类,用于保存界面数据,然后再序列化该实体类,将对象流化至本地磁盘存储,再下一次打开界面时,读取本地文件,将文件流反序列化为对象,给界面上内容进行赋值,实现界面记忆功能,即保留上次用户操作后留下的结果数据,在下次打开界面时同步显示。
b.另一个原因是方便两个进程在远程通信时方便解析对象和构建对象(例如客户端与服务器互传数据),实现跨进程传递格式化数据。
通常对象都是存在于内存中的如果我们想在远程拿到这个对象,那就要把这个对象变成一种可描述的形式(二进制或字符串)进行传输,这就是序列化。当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为对象。
我们知道,网络传输的本质就是buffer的传输, 所以在网络传输中, 序列化和反序列化是非常常见的(序列化是对象转化为字节流,反序列化是字节流转化为对象)。
三 序列化有哪些用途
由上文总结,对象的序列化主要有两种用途:
1)把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中(二进制,XML,Json);
2)在网络上传送对象的字节序列。
因此:
使用序列化主要是因为跨平台和对象存储的需求,因为网络上只允许字符串或者二进制格式,而文件需要使用二进制流格式,如果想把一个内存中的对象存储下来就必须使用序列化转换为xml(字符串)、json(字符串)或二进制(流)。
四 如何使用序列化
1-用法流程
两个的用法大致如下:
序列化:
1.得到一个存储对象的类型
2.创建一个写入文件流
3.定义要采用何种序列化的类型
4.调用序列化方法
反序列化:
1.定义一个装载对象的类型
2.创建一个读出文件流
3.定义要采用何种反序列化的类型
4.调用反序列化方法
2-序列化的方法
.NET提供了三种对象序列格式化类型:BinaryFormatter、SoapFormatter和XmlSerializer。
1) XmlSerializer 序列化对象成为XML格式
a. 序列化后在本地磁盘保存的格式为XML类型,具有可读性。
b. xml序列化必须添加 using System.Xml.Serialization。
c. xml序列化可以序列化类型中的属性、公共字段public,不能序列化私有字段private。
d. xml序列化可以在数据类型中使用[XmlIgnore]特性来标识该成员不被序列化
e. 在实例化序列化器的时候,需要指定数据的类型type
XML序列化的优点:
1. 互操作性好;
2. 不需要严格的二进制依赖;
3. 可读性强
2)BinaryFormatter
BinaryFormatter类进行序列化和反序列化,以缩略型二进制格式写到一个文件中去,速度比较快,而且写入后的文件已二进制保存有一定的保密效果。标记为NonSerialized的其他所有成员都能序列化。
a. 二进制序列化器需要添加引用:System.Runtime.Serialization;
b. 添加:usingSystem.Runtime.Serialization.Formatters.Binary;
c. 要在被序列化的类型上添加[Serializable]特性
d. 在实例化序列化器的时候,不需要指定数据的类型type
e. 二进制序列化器既可以序列化公有成员,也可以序列化私有成员
f. 如果数据类型中,有个别字段不需要序列化,要使用[NonSerializable]特性修饰
二进制序列化的优点:
1. 所有的类成员(包括只读的)都可以被序列化;
2. 性能非常好。
3)SoapFormatter
a. SoapFormatter类序列化,把数据保存成xml文件,里面除了保存内容还有些额外的soap信息。它的用法和BinaryFormatter一样。只要把序列化器BinaryFormatter都替换成SoapFormatter就行。
b. SOAP序列化与二进制序列化的区别是:SOAP序列化不能序列化泛型类型。
c. 另外就是添加名称空间:using System.Runtime.Serialization.Formatters.Soap;
d. SOAP(SimpleObject Access Protocol )简单对象访问协议是在分散或分布式的环境中交换信息的简单的协议,是一个基于XML的协议,它包括四个部分:SOAP封装(envelop),封装定义了一个描述消息中的内容是什么,是谁发送的,谁应当接受并处理它以及如何处理它们的框架;SOAP编码规则(encoding rules),用于表示应用程序需要使用的数据类型的实例; SOAP RPC表示(RPC representation),表过示远程程调用和应答的协定;SOAP绑定(binding),使用底层协议交换信息。
4)序列化对象成为Json格式
在做开发的时候,很多数据交换都是以json格式传输的。
而使用Json的时候,我们很多时候会涉及到几个序列化对象的使用:DataContractJsonSerializer,JavaScriptSerializer 和 Json.NET即Newtonsoft.Json。
大多数人都会选择性能以及通用性较好Json.NET,这个不是微软的类库,而是一个开源的世界级的Json操作类库。
Newtonsoft Json.NET也可以实现动态决定属性是否序列化。
五 序列化多方法实例
namespace WpfApplication1
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// 添加信息
/// </summary>
/// <returns></returns>
private static List<StudentInfo> AddInfo()
{
List<StudentInfo> list = new List<StudentInfo>();
for (int i = 0; i < 10; i++)
{
StudentInfo student = new StudentInfo();
student.Name = "张三" + i.ToString();
student.Number = i;
student._age = 18 + i;
student.StudentParent.father = "Tom" + i.ToString();
student.StudentParent.mather = "Lucy" + i.ToString();
list.Add(student);
}
return list;
}
/// <summary>
/// 反序列化显示数据
/// </summary>
/// <param name="data"></param>
private void ListBoxShow(object data)
{
try
{
//将反序列化后的对象强转成原序列化对象的类型
List<StudentInfo> list = (List<StudentInfo>)data;
foreach (var item in list)
{
listBox.Items.Add(" 姓名: " + item.Name + " 年龄: " + item._age + " 父亲: " + item.StudentParent.father + " 母亲: " + item.StudentParent.mather);
}
}
catch (Exception)//soap序列化转成泛型会失败
{
StudentInfo[] arry = (StudentInfo[])data;
foreach (var item in arry)
{
listBox.Items.Add(" 姓名: " + item.Name + " 年龄: " + item._age + " 父亲: " + item.StudentParent.father + " 母亲: " + item.StudentParent.mather);
}
}
}
/// <summary>
/// 1.xml序列化必须添加 using System.Xml.Serialization
/// 2.xml序列化可以序列化类型中的属性、公开字段,不能序列化私有字段
/// 3.xml序列化可以在数据类型中使用[XmlIgnore]特性来标识该成员不被序列化
/// 4.在实例化序列化器的时候,需要指定数据的类型type
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void XML_Button1(object sender, RoutedEventArgs e)
{
List<StudentInfo> list = AddInfo();
string path = @"C:\Users\Administrator\Desktop\Test\XMLSerialize.xml";
using (Stream fs = new FileStream(path, FileMode.Create))
{
XmlSerializer xml = new XmlSerializer(list.GetType());
xml.Serialize(fs, list);
}
MessageBox.Show("序列化成功");
}
/// <summary>
/// XML反序列化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void XML_Button2(object sender, RoutedEventArgs e)
{
string path = @"C:\Users\Administrator\Desktop\Test\XMLSerialize.xml";
using (Stream fs = new FileStream(path, FileMode.Open))
{
XmlSerializer xml = new XmlSerializer(typeof(List<StudentInfo>));
object data = xml.Deserialize(fs);
ListBoxShow(data);
}
}
/// <summary>
/// 1.二进制序列化器需要添加引用:System.Runtime.Serialization;还需要添加:using System.Runtime.Serialization.Formatters.Binary;
/// 2.要在被序列化的类型上添加[Serializable]特性
/// 3.在实例化序列化器的时候,不需要指定数据的类型type
/// 4.二进制序列化器既可以序列化公有成员,也可以序列化私有成员
/// 5.如果数据类型中,有个别字段不需要序列化,要使用[NonSerializable]特性修饰
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Binary_Button1(object sender, RoutedEventArgs e)
{
List<StudentInfo> list = AddInfo();
string path = @"C:\Users\Administrator\Desktop\Test\BinarySerialize.dat";
using (Stream fs = new FileStream(path, FileMode.OpenOrCreate))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, list);
}
MessageBox.Show("序列化成功");
MessageBox.Show("OK");
}
/// <summary>
/// 二进制反序列化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Binary_Button2(object sender, RoutedEventArgs e)
{
string path = @"C:\Users\Administrator\Desktop\Test\BinarySerialize.dat";
using (Stream stream = new FileStream(path, FileMode.Open))
{
BinaryFormatter binary = new BinaryFormatter();
object data = binary.Deserialize(stream);
ListBoxShow(data);
}
MessageBox.Show("OK");
}
/// <summary>
/// 1.添加引用 System.Runtime.Serialization.Formatters.Soap;
/// 2.添加 using System.Runtime.Serialization.Formatters.Soap;
/// 3.soap 序列化器不能使用泛型集合当作要序列化的数据
/// 4.soap 序列化器既可以序列化公有元素,也会序列化私有元素
/// 5.soap 序列化器在序列化时,数据必须添加[Serialized]特性
/// 6.如果不想序列化某个元素,可以加[NonSerialized]标签标注
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void soap_Button1(object sender, RoutedEventArgs e)
{
List<StudentInfo> lst = AddInfo();
string path = @"C:\Users\Administrator\Desktop\Test\SoapSerialize.ini";
using(Stream stream = new FileStream(path,FileMode.OpenOrCreate))
{
SoapFormatter sf = new SoapFormatter();
sf.Serialize(stream, lst.ToArray());
}
}
private void soap_Button2(object sender, RoutedEventArgs e)
{
string path = @"C:\Users\Administrator\Desktop\Test\SoapSerialize.ini";
using (Stream stream = new FileStream(path,FileMode.Open))
{
SoapFormatter sf = new SoapFormatter();
object obj = sf.Deserialize(stream);
ListBoxShow(obj);
}
}
/// <summary>
/// Json序列化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Json_Button1(object sender, RoutedEventArgs e)
{
List<StudentInfo> list = AddInfo();
string strJson = JsonConvert.SerializeObject(list);
string path = @"C:\Users\Administrator\Desktop\Test\JsonSerialize.json";
StreamWriter sw = new StreamWriter(path);
sw.Write(strJson);
sw.Flush();
sw.Close();
}
/// <summary>
/// Json反序列化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Json_Button2(object sender, RoutedEventArgs e)
{
string path = @"C:\Users\Administrator\Desktop\Test\JsonSerialize.json";
StreamReader sr = new StreamReader(path);
string strJson = sr.ReadToEnd();
List<StudentInfo> obj = JsonConvert.DeserializeObject<List<StudentInfo>>(strJson);
ListBoxShow(obj);
}
}
}
namespace SerializeTest
{
[Serializable]
public class StudentInfo
{
private Parent studentParent = new Parent();
public Parent StudentParent
{
get { return studentParent; }
set { studentParent = value; }
}
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public int _age;
// [NonSerialized]
private string _sex = "女";
public string Sex
{
get { return _sex; }
set { _sex = value; }
}
//[XmlIgnore]
public int Number { get; set; }
}
[Serializable]
public class Parent
{
public string father{ get; set; }
public string mather{ get; set; }
}
}