序列化和反序列化我们可能经常会听到,其实通俗一点的解释,序列化就是把一个对象保存到一个文件或数据库字段中去,反序列化就是在适当的时候把这个文件再转化成原来的对象使用。
序列化和反序列化最主要的作用有:
1、在进程下次启动时读取上次保存的对象的信息
2、在不同的AppDomain或进程之间传递数据
3、在分布式应用系统中传递数据
......
在C#中常见的序列化的方法主要也有三个:BinaryFormatter、SoapFormatter、XML序列化
本文就通过一个小例子主要说说这三种方法的具体使用和异同点
这个例子就是使用三种不同的方式把一个Book对象进行序列化和反序列化,当然这个Book类首先是可以被序列化的。
Book类
CODE:
using System;
using System.Collections;
using System.Text;
namespace SerializableTest
{
[Serializable]
public class Book
{
public Book()
{
alBookReader = new ArrayList();
}
public string strBookName;
[NonSerialized]
public string strBookPwd;
private string _bookID;
public string BookID
{
get { return _bookID; }
set { _bookID = value; }
}
public ArrayList alBookReader;
private string _bookPrice;
public void SetBookPrice(string price)
{
_bookPrice = price;
}
public void Write()
{
Console.WriteLine("Book ID:" + BookID);
Console.WriteLine("Book Name:" + strBookName);
Console.WriteLine("Book Password:" + strBookPwd);
Console.WriteLine("Book Price:" + _bookPrice);
Console.WriteLine("Book Reader:");
for (int i = 0; i < alBookReader.Count; i++)
{
Console.WriteLine(alBookReader[i]);
}
}
}
}
这个类比较简单,就是定义了一些public字段和一个可读写的属性,一个private字段,一个标记为[NonSerialized]的字段,具体会在下面的例子中体现出来
一、BinaryFormatter序列化方式
1、序列化,就是给Book类赋值,然后进行序列化到一个文件中
CODE:
Book book = new Book();
book.BookID = "1";
book.alBookReader.Add("gspring");
book.alBookReader.Add("永春");
book.strBookName = "C#强化";
book.strBookPwd = "*****";
book.SetBookPrice("50.00");
BinarySerialize serialize = new BinarySerialize();
serialize.Serialize(book);
2、反序列化
CODE:
BinarySerialize serialize = new BinarySerialize();
Book book = serialize.DeSerialize();
book.Write();
3、测试用的
BinarySerialize类
CODE:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace SerializableTest
{
public class BinarySerialize
{
string strFile = "c:""book.data";
public void Serialize(Book book)
{
using (FileStream fs = new FileStream(strFile, FileMode.Create))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fs, book);
}
}
public Book DeSerialize()
{
Book book;
using (FileStream fs = new FileStream(strFile, FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
book = (Book)formatter.Deserialize(fs);
}
return book;
}
}
}
主要就是调用System.Runtime.Serialization.Formatters.Binary空间下的BinaryFormatter类进行序列化和反序列化,以缩略型二进制格式写到一个文件中去,速度比较快,而且写入后的文件已二进制保存有一定的保密效果。
调用反序列化后的截图如下:
也就是说除了标记为NonSerialized的其他所有成员都能序列化
二、SoapFormatter序列化方式
调用序列化和反序列化的方法和上面比较类似,我就不列出来了,主要就看看SoapSerialize类
SoapSerialize类
CODE:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Soap;
namespace SerializableTest
{
public class SoapSerialize
{
string strFile = "c:""book.soap";
public void Serialize(Book book)
{
using (FileStream fs = new FileStream(strFile, FileMode.Create))
{
SoapFormatter formatter = new SoapFormatter();
formatter.Serialize(fs, book);
}
}
public Book DeSerialize()
{
Book book;
using (FileStream fs = new FileStream(strFile, FileMode.Open))
{
SoapFormatter formatter = new SoapFormatter();
book = (Book)formatter.Deserialize(fs);
}
return book;
}
}
}
主要就是调用System.Runtime.Serialization.Formatters.Soap空间下的SoapFormatter类进行序列化和反序列化,使用之前需要应用System.Runtime.Serialization.Formatters.Soap.dll(.net自带的)
序列化之后的文件是Soap格式的文件(简单对象访问协议(Simple Object Access Protocol,SOAP),是一种轻量的、简单的、基于XML的协议,它被设计成在WEB上交换结构化的和固化的信息。 SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议(HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。SOAP使用基于XML的数据结构和超文本传输协议(HTTP)的组合定义了一个标准的方法来使用Internet上各种不同操作环境中的分布式对象。)
调用反序列化之后的结果和方法一相同
三、XML序列化方式
调用序列化和反序列化的方法和上面比较类似,我就不列出来了,主要就看看XmlSerialize类
XmlSerialize类
CODE:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml.Serialization;
namespace SerializableTest
{
public class XmlSerialize
{
string strFile = "c:""book.xml";
public void Serialize(Book book)
{
using (FileStream fs = new FileStream(strFile, FileMode.Create))
{
XmlSerializer formatter = new XmlSerializer(typeof(Book));
formatter.Serialize(fs, book);
}
}
public Book DeSerialize()
{
Book book;
using (FileStream fs = new FileStream(strFile, FileMode.Open))
{
XmlSerializer formatter = new XmlSerializer(typeof(Book));
book = (Book)formatter.Deserialize(fs);
}
return book;
}
}
}
从这三个测试类我们可以看出来其实三种方法的调用方式都差不多,只是具体使用的类不同
xml序列化之后的文件就是一般的一个xml文件:
book.xml
CODE:
<?xml version="1.0"?>
<Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<strBookName>C#强化</strBookName>
<strBookPwd>*****</strBookPwd>
<alBookReader>
<anyType xsi:type="xsd:string">gspring</anyType>
<anyType xsi:type="xsd:string">永春</anyType>
</alBookReader>
<BookID>1</BookID>
</Book>
输出截图如下:
也就是说采用xml序列化的方式只能保存public的字段和可读写的属性,对于private等类型的字段不能进行序列化
关于循环引用:
比如在上面的例子Book类中加入如下一个属性:
public Book relationBook;
在调用序列化时使用如下方法:
CODE:
Book book = new Book();
book.BookID = "1";
book.alBookReader.Add("gspring");
book.alBookReader.Add("永春");
book.strBookName = "C#强化";
book.strBookPwd = "*****";
book.SetBookPrice("50.00");
Book book2 = new Book();
book2.BookID = "2";
book2.alBookReader.Add("gspring");
book2.alBookReader.Add("永春");
book2.strBookName = ".NET强化";
book2.strBookPwd = "*****";
book2.SetBookPrice("40.00");
book.relationBook = book2;
book2.relationBook = book;
BinarySerialize serialize = new BinarySerialize();
serialize.Serialize(book);
这样就会出现循环引用的情况,对于BinarySerialize和SoapSerialize可以正常序列化(.NET内部进行处理了),对于XmlSerialize出现这种情况会报错:"序列化类型 SerializableTest.Book 的对象时检测到循环引用。"
C#序列化和反序列化程序都是基于工厂模式下的,那么C#序列化和反序列化到底有什么不同之处么?那么本文就向你详细介绍C#序列化和反序列化程序的区别及其应用。
C#序列化和反序列化,两者的程序处理方式基本一致,都是基于工厂模式的,所谓C#序列化就是是将对象转换为容易传输的格式的过程,一般情况下转化打流文件,放入内存或者IO文件中。例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象,或者和其它应用程序共享使用。相反的,反序列化根据流重新构造对象。.NET自带的有两种序列化对象的方式,Xml和binary的,XML 序列化不转换方法、索引器、私有字段或只读属性(只读集合除外)。要序列化对象的所有字段和属性(公共的和私有的),请使用 BinaryFormatter,而不要使用 XML 序列化。
C#序列化和反序列化的实例应用剖析:
二进制的C#序列化的方式:
例如我们有个对象:
[Serializable]public class ClassToSerialize{ public int id=100; public string name="Name"; } 需要序列化该对象,必须在给该类加上Serializable的属性,然后创建一个序列化写入的流:FileStream fileStream = new FileStream("temp.dat", FileMode.Create);然后创建二进制格式器:BinaryFormatter b=new BinaryFormatter();然后是序列化:b.Serialize(fileStream,c);,然后关闭保存流。(可以见下面的例子)
读取一个已经被序列化的对象的时候:操作方式一样,只是
FileStream fileStream = new FileStream( "temp.dat", FileMode.Open, FileAccess.Read, FileShare.Read); ClassToSerialize c = (ClassToSerialize)b.Deserialize(fileStream); 然后就可以读取了,完整的例子是:
using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; public class SerialTest{ public void SerializeNow(){ ClassToSerialize c=new ClassToSerialize(); FileStream fileStream = new FileStream( "temp.dat", FileMode.Create); BinaryFormatter b=new BinaryFormatter(); b.Serialize(fileStream,c); fileStream.Close(); } public void DeSerializeNow(){ ClassToSerialize c=new ClassToSerialize(); FileStream fileStream = new FileStream( "temp.dat", FileMode.Open, FileAccess.Read, FileShare.Read); BinaryFormatter b=new BinaryFormatter(); //SoapFormatter c=(ClassToSerialize)b.Deserialize(fileStream); Console.WriteLine(c.name); fileStream.Close(); } public static void Main(string[] s){ SerialTest st=new SerialTest(); st.SerializeNow(); st.DeSerializeNow(); } } [Serializable] public class ClassToSerialize{ public int id=100; public string name="Name"; } 这就是自带的序列化和反序列的操作,但是,很多情况下,一个对象比较大,而且很多私有的属性和方法我们不需要,例如在原型模式里面序列化的话,只需要序列Clone方法和一些属性,私有的方法无需要,还例如在读取大规模的IO的时候,读取操作完全不需要... 这时候就需要自己集成重写序列的ISerializable接口:
实现该接口需要两个注意的,一个就是构造函数,主要是为了反序列,另一个就是GetObjectData,主要是执行序列化,例如我们现在有一个Employee类需要序列化
[Serializable()] //Set this attribute to all the classes that want to serialize public class Employee : ISerializable //derive your class from ISerializable { public int EmpId; public string EmpName; [NonSerialized()] public string NoSerialString="NoSerialString-Test"; } 需要注意的是我这里的NoSerialString属性前面有[NonSerialized()],就是说默认并不序列化这个属性,而是使用默认值 。
首先是构造函数:
public Employee(SerializationInfo info, StreamingContext ctxt) { EmpId = (int)info.GetValue( "EmployeeId", typeof(int)); EmpName = (String)info.GetValue( "EmployeeName", typeof(string)); //NoSerialString = //(String)info.GetValue("NoSerialString", typeof(string)); } 然后是C#序列化方法,就是当写入流的时候怎么保存的:
public void GetObjectData(SerializationInfo info, StreamingContext ctxt) { //You can use any custom name for your name-value pair. // But make sure you // read the values with the same name. //For ex:- If you write EmpId as "EmployeeId" // then you should read the same with "EmployeeId" info.AddValue("EmployeeId", EmpId); info.AddValue("EmployeeName", EmpName); } 把上面两个方法写入到Employee类,然后写个测试的程序:
public class ObjSerial{ public static void Main(String[] args){ Employee mp = new Employee(); mp.EmpId = 10; mp.EmpName = "Omkumar"; mp.NoSerialString = "你好啊"; //C#序列化和反序列化之序列化 Stream stream = File.Open("EmployeeInfo.osl", FileMode.Create); BinaryFormatter bformatter = new BinaryFormatter(); Console.WriteLine("Writing Employee Information"); bformatter.Serialize(stream, mp); stream.Close(); mp = null; //C#序列化和反序列化之反序列 stream = File.Open("EmployeeInfo.osl", FileMode.Open); bformatter = new BinaryFormatter(); Console.WriteLine("Reading Employee Information"); mp = (Employee)bformatter.Deserialize(stream); stream.Close(); Console.WriteLine( "Employee Id: {0}",mp.EmpId.ToString()); Console.WriteLine( "Employee Name: {0}",mp.EmpName); Console.WriteLine( "Employee NoSerialString: {0}",mp.NoSerialString); } } C#序列化和反序列化程序执行的结果是:
Writing Employee Information Reading Employee Information Employee Id: 10 Employee Name: Omkumar Employee NoSerialString: NoSerialString-Test 看到Employee NoSerialString:属性的值没有,它保持默认值,没有序列化。
前些天看到“序列化”这个概念,大意就是把对象保存为一个文件,下次再使用时,反序列化一下就OK了,第二天一大早到公司立马做了个Demo,哇,不错,非常之不错,使用此方法后,根本没有延迟的现象。今天终于应用到这个项目中了,同志们再也不用等那10秒了。
using System;
using System.IO;
using System.Windows.Forms;
using System.Runtime.Serialization.Formatters.Binary;
using System.Data;
//from:http://www.ecjtu.org/forum/read.php?tid-12933.html
//反序列化:SerializeTree.TreeViewDataAccess.LoadTreeViewData(treeView1,"C://treeview.txt");
//序列化: SerializeTree.TreeViewDataAccess.SaveTreeViewData(treeView1,"C://treeview.txt");
namespace SerializeTree
{
/// <summary>
/// TreeView串行化类
/// </summary>
public class TreeViewDataAccess
{
public TreeViewDataAccess() { }
/// <summary>
/// TreeViewData
/// </summary>
[Serializable()]
public struct TreeViewData
{
public TreeNodeData[] Nodes;
/// <summary>
/// 递归初始化TreeView数据
/// </summary>
/// <param name="treeview"></param>
public TreeViewData(TreeView treeview)
{
Nodes = new TreeNodeData[treeview.Nodes.Count];
if (treeview.Nodes.Count == 0)
{
return;
}
for (int i = 0; i <= treeview.Nodes.Count - 1; i++)
{
Nodes[i] = new TreeNodeData(treeview.Nodes[i]);
}
}
/// <summary>
/// 通过TreeViewData弹出TreeView
/// </summary>
/// <param name="treeview"></param>
public void PopulateTree(TreeView treeview)
{
if (this.Nodes == null || this.Nodes.Length == 0)
{
return;
}
treeview.BeginUpdate();
for (int i = 0; i <= this.Nodes.Length - 1; i++)
{
treeview.Nodes.Add(this.Nodes[i].ToTreeNode());
}
treeview.EndUpdate();
}
}
/// <summary>
/// TreeNodeData
/// </summary>
[Serializable()]
public struct TreeNodeData
{
public string Text;
public int ImageIndex;
public int SelectedImageIndex;
public bool Checked;
public bool Expanded;
public object Tag;
public TreeNodeData[] Nodes;
/// <summary>
/// TreeNode构造函数
/// </summary>
/// <param name="node"></param>
public TreeNodeData(TreeNode node)
{
this.Text = node.Text;
this.ImageIndex = node.ImageIndex;
this.SelectedImageIndex = node.SelectedImageIndex;
this.Checked = node.Checked;
this.Expanded = node.IsExpanded;
this.Nodes = new TreeNodeData[node.Nodes.Count];
if ((!(node.Tag == null)) && node.Tag.GetType().IsSerializable)
{
this.Tag = node.Tag;
}
else
{
this.Tag = null;
}
if (node.Nodes.Count == 0)
{
return;
}
for (int i = 0; i <= node.Nodes.Count - 1; i++)
{
Nodes[i] = new TreeNodeData(node.Nodes[i]);
}
}
/// <summary>
/// TreeNodeData返回TreeNode
/// </summary>
/// <returns></returns>
public TreeNode ToTreeNode()
{
TreeNode ToTreeNode = new TreeNode(this.Text, this.ImageIndex, this.SelectedImageIndex);
ToTreeNode.Checked = this.Checked;
ToTreeNode.Tag = this.Tag;
if (this.Expanded)
{
ToTreeNode.Expand();
}
if (this.Nodes == null && this.Nodes.Length == 0)
{
return null;
}
if (ToTreeNode != null && this.Nodes.Length == 0)
{
return ToTreeNode;
}
for (int i = 0; i <= this.Nodes.Length - 1; i++)
{
ToTreeNode.Nodes.Add(this.Nodes[i].ToTreeNode());
}
return ToTreeNode;
}
}
/// <summary>
/// 加载TreeView
/// </summary>
/// <param name="treeView"></param>
/// <param name="path"></param>
public static void LoadTreeViewData(TreeView treeView, string path)
{
BinaryFormatter ser = new BinaryFormatter();
Stream file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
TreeViewData treeData = ((TreeViewData)(ser.Deserialize(file)));
treeData.PopulateTree(treeView);
file.Close();
}
/// <summary>
/// 保存TreeView到文件
/// </summary>
/// <param name="treeView"></param>
/// <param name="path"></param>
public static void SaveTreeViewData(TreeView treeView, string path)
{
BinaryFormatter ser = new BinaryFormatter();
Stream file = new FileStream(path, FileMode.Create);
ser.Serialize(file, new TreeViewData(treeView));
file.Close();
}
}
//柳永法加的,序列化,及反序列化DataTable
class SerializeDataTable
{
public static DataTable LoadDataTable(string path)
{
BinaryFormatter ser = new BinaryFormatter();
Stream file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
DataTable dt = (DataTable)ser.Deserialize(file);
file.Close();
return dt;
}
public static void SaveDataTable(DataTable dt, string path)
{
BinaryFormatter ser = new BinaryFormatter();
Stream file = new FileStream(path, FileMode.Create);
ser.Serialize(file, dt);
file.Close();
}
}
}