mysql 序列化存储_如何将类序列化并直接存储入数据库

本文探讨了在MySQL数据库中存储序列化对象的方法,对比了二进制序列化器和XML序列化器的优缺点。文章还介绍了.NET框架下的序列化机制,包括对类的要求、基本序列化和自定义序列化。通过示例代码,展示了如何使用XML和二进制格式器进行序列化和反序列化,并讨论了自定义序列化以适应类结构变化的场景。
摘要由CSDN通过智能技术生成

本文将从这两个格式器入手,先向大家介绍分别用它们如何实现序列化和反序列化,然后比较两种格式器的不同点。接着我会向大家介绍实现序列化对对象类型的一些要求,同时还要向大家介绍两种不同的序列化方式:基本序列化(Basic Serialization)和自定义序列化(Custom Serialization)。最后,我还会给大家介绍一个实例程序以加深大家对序列化机制的理解程度。

程序员在编写应用程序的时候往往要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络中的另一台计算机上以实现通讯。这个将程序数据转化成能被存储并传输的格式的过程被称为"序列化"(Serialization),而它的逆过程则可被称为"反序列化"(Deserialization)。

.Net框架对序列化机制具有非常好的支持,它提供了两个名字空间(namespace):System.Runtime.Serialization和System.Runtime.Serialization.Formatters以完成序列化机制的大部分功能。系列化这项技术可以应用在将程序产生的结果数据存储到文件系统中,但是它更主要的应用是在于.Net Remoting和Web服务的实现上。

序列化机制的实现是依靠格式器(Formatter)而完成的,它是一个从System.Runtime.Serialization.IFormatter继承下来的类的对象。格式器完成了将程序数据转化到能被存储并传输的格式的工作,同时也完成了将数据转化回来的工作。.Net框架为程序员提供了两种类型的格式器,一种通常是应用于桌面类型的应用程序的,它一个是System.Runtime.Serialization.Formatters.Binary.BinaryFormatter类的对象,而另一种则更主要的应用于.Net Remoting和XML Web服务等领域的,它一个是System.Runtime.Serialization.Formatters.Soap.SoapFormatter类的对象。从它们的名称来看,我们不妨将它们分别称为二进制格式器和XML格式器。

一、二进制格式器 vs XML格式器

下面我先向大家介绍两种不同的格式器,分别用它们如何实现序列化机制和反序列化机制,请看下面的代码:

#region Binary Serializers

public static System.IO.MemoryStream SerializeBinary(objectrequest) {

System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer=

newSystem.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

System.IO.MemoryStream memStream= newSystem.IO.MemoryStream();

serializer.Serialize(memStream, request);returnmemStream;

}public static objectDeSerializeBinary(System.IO.MemoryStream memStream) {

memStream.Position=0;

System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer=

newSystem.Runtime.Serialization.Formatters.Binary.BinaryFormatter();object newobj =deserializer.Deserialize(memStream);

memStream.Close();returnnewobj;

}#endregion

#region XML Serializers

public static System.IO.MemoryStream SerializeSOAP(objectrequest) {

System.Runtime.Serialization.Formatters.Soap.SoapFormatter serializer=

newSystem.Runtime.Serialization.Formatters.Soap.SoapFormatter();

System.IO.MemoryStream memStream= newSystem.IO.MemoryStream();

serializer.Serialize(memStream, request);returnmemStream;

}public static objectDeSerializeSOAP(System.IO.MemoryStream memStream) {objectsr;

System.Runtime.Serialization.Formatters.Soap.SoapFormatter deserializer=

newSystem.Runtime.Serialization.Formatters.Soap.SoapFormatter();

memStream.Position=0;

sr=deserializer.Deserialize(memStream);

memStream.Close();returnsr;

}#endregion

从上面的代码我们可以发现无论运用哪种格式器,其基本的过程都是一样的,而且都是非常容易实现的,唯一的不同就是定义格式器的类型不同。不过在实际的应用中,二进制格式器往往应用于一般的桌面程序和网络通讯程序中,而XML格式器禀承了XML技术的优点,大多数被应用于.Net Remoting和XML Web服务等领域。下面我们来分析一下两种格式器各自的优点。

二进制序列化的优点:

1. 所有的类成员(包括只读的)都可以被序列化;

2. 性能非常好。

XML序列化的优点:

1. 互操作性好;

2. 不需要严格的二进制依赖;

3. 可读性强。

通过分析上面的代码,我们知道了选择二进制序列化的方式还是选择XML序列化的方式仅仅是对不同的格式器进行选择而已。你可以根据实际的需要选择相应的格式器完成序列化和反序列化工作。同时请注意,代码中的序列化函数和反序列化函数仅仅是在调用Serialize()和Deserialize()这两个核心函数上产生了差别,即它们的参数不同。因此以上的代码完成了一些最最基本但是很重要的功能,你可以将它们运用在你的程序中,或是将其进行适当扩充以满足程序的特定需要。

二、序列化机制对类的要求

如果你要对一个对象进行序列化,那么你必须将它的类型标记为[Serializable()],该操作是通过SerializableAttribute属性来实现的。将SerializableAttribute属性应用于一种数据类型可表明该数据类型的实例可以被序列化。如果正在序列化的对象图中的任何类型未应用SerializableAttribute属性,公共语言运行库则会引发SerializationException。默认情况下,类型中由SerializableAttribute标记的所有公共和私有字段都会进行序列化,除非该类型实现ISerializable接口来重写序列化进程(通过实现该接口我们便可以实现将在后面介绍的"自定义序列化")。默认的序列化进程会排除用NonSerializedAttribute属性标记的字段,即你可以将该类型标记为[NonSerialized()]以表明它是不可以被序列化的。如果可序列化类型的字段包含指针、句柄或其他某些针对于特定环境的数据结构,并且不能在不同的环境中以有意义的方式重建,则最好将NonSerializedAttribute属性应用于该字段。有关序列化的更多信息,请参阅System.Runtime.Serialization名字空间中的相关内容。

下面我给大家介绍一个例子,以显示如何正确的运用SerializableAttribute属性和NonSerializedAttribute属性。该程序中运用到了XML格式器,不过同时给出了二进制格式器为参考(程序中将其用"//"标注),其实现的结果是一样的。该程序实现的功能是在序列化和反序列化操作前后测试对象因包含了[NonSerialized()]的字段而显示不同的屏幕打印结果。其代码如下:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

usingSystem;usingSystem.IO;usingSystem.Runtime.Serialization;usingSystem.Runtime.Serialization.Formatters.Soap;//using System.Runtime.Serialization.Formatters.Binary;

public classTest {public static voidMain() {//创建一个新的测试对象

TestSimpleObject obj = newTestSimpleObject();

Console.WriteLine("Before serialization the object contains:");

obj.Print();//创建一个文件"data.xml"并将对象序列化后存储在其中

Stream stream = File.Open("data.xml", FileMode.Create);

SoapFormatter formatter= newSoapFormatter();//BinaryFormatter formatter = new BinaryFormatter();

formatter.Serialize(stream, obj);

stream.Close();//将对象置空

obj = null;//打开文件"data.xml"并进行反序列化得到对象

stream = File.Open("data.xml", FileMode.Open);

formatter= newSoapFormatter();//formatter = new BinaryFormatter();

obj=(TestSimpleObject)formatter.Deserialize(stream);

stream.Close();

Console.WriteLine("");

Console.WriteLine("After deserialization the object contains:");

obj.Print();

}

}//一个要被序列化的测试对象的类

[Serializable()]public classTestSimpleObject {public intmember1;public stringmember2;public stringmember3;public doublemember4;//标记该字段为不可被序列化的

[NonSerialized()] public stringmember5;publicTestSimpleObject() {

member1= 11;

member2= "hello";

member3= "hello";

member4= 3.14159265;

member5= "hello world!";

}public voidPrint() {

Console.WriteLine("member1 = '{0}'", member1);

Console.WriteLine("member2 = '{0}'", member2);

Console.WriteLine("member3 = '{0}'", member3);

Console.WriteLine("member4 = '{0}'", member4);

Console.WriteLine("member5 = '{0}'", member5);

}

}

View Code

三、基本序列化、自定义序列化

.Net框架为我们提供了两种方式的序列化:一种为基本序列化、另一种为自定义序列化。值得注意的是,序列化的方式和前面提到的序列化的格式是不同的概念。序列化的方式是指.Net框架将程序的数据转化为能被存储并传输的格式的实际过程,它是不管程序员运用了何种类型的格式器的(二进制格式器还是XML格式器)。而序列化的格式则指程序的数据是被转化成二进制格式了还是被转化成XML格式了。

完成序列化的最简单的方法便是让.Net框架自动为我们完成整个过程,而我们不必去管它内部是如何具体实现的,这种方法便是前面提到的"基本序列化"。在这种方式下,我们需要做的仅仅是将类标记上[Serializable()]属性。然后.Net框架便调用该类的对象并将它转化为所需的格式。同时你还可以控制其中的某些字段不被序列化,方法就是前面所述的将该字段标记上[NonSerialized()]属性。这样,最最简单和基本的序列化工作就完成了,不过其内部是如何实现的你是不得而知的,同时你也不能进一步控制序列化过程的程序行为。

如果你要获得对序列化的更大的控制权,那么你就得使用"自定义序列化"的方式。通过使用这种方式,你可以完全的控制类的哪些部分能被序列化而哪些部分不能,同时你还可以控制如何具体的进行序列化。运用该方式的好处就是能克服基本序列化所会遇到的问题。我们在运用基本序列化将一个类的对象序列化完毕并存储在文件中后,假设该对象原来有三个字段,如果此时该对象增加了一个字段,那么再将该对象从文件中反序列化出来时会发生字段数不一致的错误。这样的问题是基本序列化所不能解决的,只能运用自定义序列化的方式来解决。

在介绍自定义序列化之前,我先给出介绍过程中所要用到的实例程序的代码。这是一个时间安排程序,其中要用到将不同的时间格式进行转化的操作。所以运用序列化的机制能很好的解决这个问题。

usingSystem;usingSystem.Runtime.Serialization;namespaceSerializationSample {

[Serializable()]public classSchedule {protectedSystem.DateTime start;protectedSystem.DateTime end;//每个时间间隔所要增加的毫秒数

protected longinterval;public System.DateTime Start {get{return start;}set{start=value;}}public System.DateTime End {get{return end;}set{end=value;}}public long Interval {get{return interval;}set{interval=value;}}public Schedule(System.DateTime Start, System.DateTime End, longInterval) {

start=Start;

end=End;

interval=Interval;

}//如果已经到了结束的时间,则返回结束时间,否则返回下一次运行的时间

publicSystem.DateTime NextRunTime {get{

System.TimeSpan ts= new System.TimeSpan(end.Ticks-System.DateTime.Now.Ticks);if(ts.Milliseconds>0) {returnSystem.DateTime.Now.AddMilliseconds(interval);

}else{returnend;

}

}

}

}

}

自定义序列化:

下面我就向大家介绍自定义序列化以及反序列化的具体过程。首先,程序的类必须实现System.Runtime.Serialization.ISerializable接口,该接口的功能就是允许对象控制其自己的序列化和反序列化过程。所以我们得重新定义上面的类:

[Serializable()]

public class ScheduleCustom : System.Runtime.Serialization.ISerializable

接下来,我们必须对该接口调用GetObjectData()的实现,也即我们必须在上面的类中给出GetObjectData()的具体实现。其函数原型如下:

void GetObjectData(SerializationInfo info, StreamingContext context);

上面的类中GetObjectData()的具体实现如下:

public voidGetObjectData(SerializationInfo info,StreamingContext context) {//运用info对象来添加你所需要序列化的项

info.AddValue("start", start);

info.AddValue("end", end);

info.AddValue("interval", interval);

}

然而对于这么一个简单的方法,读者可能不能理会到系列化带来的强大功能,所以下面我就给这个方法添加一些东西。如果在系列化过程中我们要查看类型为DateTime的"start"属性的输出的话,其结果会是.Net框架默认的格式:

而对于没有.Net框架的用户,或是在其他时间区域内的用户而言,这么一个格式的时间可能是非常难以理解的,所以我们有必要将时间的格式转化为格林威治标准时间格式,于是修改GetObjectData()方法如下:

public voidGetObjectData(SerializationInfo info,StreamingContext context) {//运用info对象来添加你所需要序列化的项//同时,将"start"和"end"属性的时间格式转化为格林威治标准时间格式

info.AddValue("start", System.TimeZone.CurrentTimeZone.ToUniversalTime(start));

info.AddValue("end", System.TimeZone.CurrentTimeZone.ToUniversalTime(end));

info.AddValue("interval", interval);

info.AddValue("timeformat", "utc");

}

这样一来,我们在系列化过程中查看"start"属性时就会得到如下结果:

同时请注意我们在GetObjectData()方法中添加的一个名为"timeformat"的额外属性,通过它我们可以方便的知道系列化过程中所使用的时间格式。如果有兴趣的话,你还可以从System.Globalization.DateTimeFormatInfo这个名字空间中获取更多有关时间格式的信息。

自定义反序列化:

你可以通过调用一个自定义的构造函数来完成自定义反序列化的操作。该构造函数的定义如下:

public ScheduleCustom (SerializationInfo info,StreamingContext context);

在上面的类中,我们的ScheduleCustom()方法将完成把时间格式从格林威治标准时间格式反序列化为当地时间的格式的操作,其函数实现如下:

public ScheduleCustom (SerializationInfo info,StreamingContext context) {

this.start = info.GetDateTime("start").ToLocalTime();

this.end = info.GetDateTime("end").ToLocalTime();

this.interval = info.GetInt32("interval");

}

在完成自定义序列化和自定义反序列化后,我们的时间安排程序变成了如下形式:

usingSystem;usingSystem.Runtime.Serialization;namespaceSerializationSample {

[Serializable()]public classScheduleCustom : System.Runtime.Serialization.ISerializable {protectedSystem.DateTime start;protectedSystem.DateTime end;//每个时间间隔所要增加的毫秒数

protected longinterval;public System.DateTime Start {get{return start;}set{start=value;}}public System.DateTime End {get{return end;}set{end=value;}}public long Interval {get{return interval;}set{interval=value;}}public ScheduleCustom(System.DateTime Start, System.DateTime End, longInterval) {

start=Start;

end=End;

interval=Interval;

}//如果已经到了结束的时间,则返回结束时间,否则返回下一次运行的时间

publicSystem.DateTime NextRunTime {get{

System.TimeSpan ts= new System.TimeSpan(end.Ticks-System.DateTime.Now.Ticks);if(ts.Milliseconds>0) {returnSystem.DateTime.Now.AddMilliseconds(interval);

}else{returnend;

}

}

}public voidGetObjectData(SerializationInfo info,StreamingContext context) {//运用info对象来添加你所需要序列化的项//同时,将"start"和"end"属性的时间格式转化为格林威治标准时间格式

info.AddValue("start", System.TimeZone.CurrentTimeZone.ToUniversalTime(start));

info.AddValue("end", System.TimeZone.CurrentTimeZone.ToUniversalTime(end));

info.AddValue("interval", interval);

info.AddValue("timeformat", "utc");

}publicScheduleCustom (SerializationInfo info,StreamingContext context) {this.start = info.GetDateTime("start").ToLocalTime();this.end = info.GetDateTime("end").ToLocalTime();this.interval = info.GetInt32("interval");

}

}

}

总结以上内容,前三部分向大家介绍了.Net框架下系列化机制的一些基本概念和基本的运用方法,读者在读完本文后,应该对以下几个概念有个初步了解:二进制系列化、XML系列化、基本序列化和自定义系列化,并应能够完成一些基本的系列化应用。最后,希望大家能合理有效的运用系列化机制并发挥它的功效以更好地满足实际工作需要。

参考代码1

可以使用.net提供的序列化和反序列化方法来实现,你可将对象序列化成XML字符串,然后存入数据库中,当你要使用对象的时候,再把数据库中保存字符串反序列化成对象就可以使用了,以下为示例代码:

public classCat

{public string Color { get; set; }public int Speed { get; set; }public string Name{ get; set; }

}//序列化

var cat1=new Cat{Color="Write",Speed=50,Name="MiMi"};

XmlSerializer ser= new XmlSerializer(typeof(Cat));

MemoryStream ms= newMemoryStream();

ser.Serialize(ms, cat1);string xmlString =Encoding.UTF8.GetString(ms.ToArray());//xmlString就是你要保存到数据库的字符串//反序列化

XmlSerializer dser = new XmlSerializer(typeof(Cat));//xmlString是你从数据库获取的字符串

Stream xmlStream = newMemoryStream(Encoding.UTF8.GetBytes(xmlString));

Cat cat2=dser.Deserialize(xmlStream) as Cat;//cat2 就是你要得到的class对象

参考代码2

创建项目

1.      添加一个名为RWTest的表到 SQL Server MYTest 数据库。 表字段设置如下:

a.      唯一标识字段名称为"ID",类型为Int。

b.      名称为"Description"的VarChar类型的字段,字段长度为50。

c.      名称为"Data" 的varbinary(Max) 类型的字段。

2.      启动 Visual Studio .NET, 并创建一个新的 Visual C# Windows 应用程序项目。

3.      从工具栏中拖两个Button 控件到默认窗体, Form1。

4.      在属性窗口中修改Name为buttonFileToDB, Text 属性为从文件保存到数据库, 然后修改Name为buttonDBToFile ,Text 属性为从数据库保存到文件。

usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Text;usingSystem.Windows.Forms;usingSystem.Data.SqlClient;usingSystem.IO;usingSystem.Collections;usingSystem.Runtime.Serialization.Formatters.Binary;//数据库说明:MyTest数据库,RWTest表,包含3列:ID(int),Description(varchar(50), Data(varbinary(max))

namespaceRWArrayListSQL

{public partial classForm1 : Form

{

ArrayList arrayList= newArrayList();publicForm1()

{

InitializeComponent();for (int i = 0; i < 100; i++)

{

PTData ptData= newPTData();

ptData.PTID= i + 1;

ptData.PTName= Convert.ToString(i + 1);

ptData.PT_Data= i + 1;

arrayList.Add(ptData);

}

}private void buttonFileToDB_Click(objectsender, EventArgs e)

{

SqlConnection sqlConnection= new SqlConnection("Data Source=liuxueqin;Initial Catalog=MyTest;Integrated Security=True");

SqlDataAdapter sqlDataAdapter= new SqlDataAdapter("Select * from RWTest", sqlConnection);

SqlCommandBuilder sqlCommandBuilder= newSqlCommandBuilder(sqlDataAdapter);

System.Data.DataSet dataSet= new DataSet("RWTest");

sqlDataAdapter.MissingSchemaAction= MissingSchemaAction.AddWithKey;//确定现有 DataSet 架构与传入数据不匹配时需要执行的操作。//定义一个流

Stream stream = newMemoryStream();//定义一个格式化器

BinaryFormatter bf = newBinaryFormatter();foreach (object obj inarrayList)

{

bf.Serialize(stream, obj);//序列化

}byte[] array = null;

array= new byte[stream.Length];//将二进制流写入数组

stream.Position = 0;

stream.Read(array,0, (int)stream.Length);//关闭流

stream.Close();try{

sqlDataAdapter.Fill(dataSet,"RWTest");//DataRow表示DataTable中的一行数据

System.Data.DataRow dataRow1;

dataRow1= dataSet.Tables["RWTest"].NewRow();

dataRow1["ID"] = 1;

dataRow1["Description"] = "This would be description text";

dataRow1["Data"] =array;

dataSet.Tables["RWTest"].Rows.Add(dataRow1);

sqlDataAdapter.Update(dataSet,"RWTest");

sqlConnection.Close();

MessageBox.Show("写入数据库成功!", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Information);

}catch(Exception ex)

{if (sqlConnection.State ==ConnectionState.Open)

{

sqlConnection.Close();

}

MessageBox.Show("写入数据库失败"+ex.Message, "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

}private void buttonDBToFile_Click(objectsender, EventArgs e)

{

SqlConnection sqlConnection= new SqlConnection("Data Source=liuxueqin;Initial Catalog=MyTest;Integrated Security=True");

SqlDataAdapter sqlDataAdapter= new SqlDataAdapter("Select * from RWTest", sqlConnection);

SqlCommandBuilder sqlCommandBuilder= newSqlCommandBuilder(sqlDataAdapter);

DataSet dataSet= new DataSet("RWTest");

sqlDataAdapter.Fill(dataSet,"RWTest");

DataRow myRow;

myRow= dataSet.Tables["RWTest"].Rows[0];byte[] b = null;

b= (byte[])myRow["Data"];//定义一个流

MemoryStream stream = newMemoryStream(b);//定义一个格式化器

BinaryFormatter bf = newBinaryFormatter();while (stream.Position !=stream.Length)

{

arrayList.Add(bf.Deserialize(stream));//反序列化

}

stream.Close();for(int i=0;i<5;i++) //信息提示,是否正确从数据库中取出了ArrayList链表

MessageBox.Show(((PTData)arrayList[i]).PTName, "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Information);if (sqlConnection.State ==ConnectionState.Open)

{

sqlConnection.Close();

}

MessageBox.Show("从数据库读出数据成功!", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Information);

}

}//在类的上面增加了属性:Serializable.(如果不加这个属性,将抛出SerializationException异常)。在不继承自接口ISerializable的情况下,通过增加[Serializable]属性可以允许该类可以被序列化。

[Serializable]classPTData

{publicPTData()

{

PTID= 1;

PTName= "None";

PT_Data= 1.0;

}public intPTID;public stringPTName;public doublePT_Data;

}

}

参考文章

在Python中,将列表序列化后保存到MySQL数据库通常涉及以下步骤: 1. 序列化列表:首先需要将列表序列化为一个字符串格式,这可以通过多种方式实现,比如使用JSON格式序列化,因为JSON格式易于阅读并且在多种编程语言间具有良好的兼容性。可以使用Python内置的`json`模块来完成这个任务。 2. 连接MySQL数据库:使用一个适合Python的数据库连接库,如`mysql-connector-python`或`pymysql`,来建立与MySQL数据库的连接。 3. 创建数据库表:确保数据库中有一个表用来存储序列化后的数据。如果表不存在,你需要创建一个表。表的结构至少应该包含一个用于存储序列化字符串的字段,通常是`VARCHAR`或`TEXT`类型。 4. 插数据:将序列化后的字符串插数据库表中的对应字段。 以下是一个简单的示例代码,展示了如何使用`json`模块和`mysql-connector-python`库来完成这个过程: ```python import json import mysql.connector # 假设我们有一个列表数据 data_list = ["item1", "item2", "item3"] # 将列表序列化为JSON字符串 serialized_data = json.dumps(data_list) # 连接到MySQL数据库 db = mysql.connector.connect( host="localhost", user="yourusername", password="yourpassword", database="yourdatabase" ) # 创建一个cursor对象 cursor = db.cursor() # 创建表,如果表不存在的话 cursor.execute(""" CREATE TABLE IF NOT EXISTS data_table ( id INT AUTO_INCREMENT PRIMARY KEY, data TEXT NOT NULL ) """) # 插序列化后的数据到表中 cursor.execute("INSERT INTO data_table (data) VALUES (%s)", (serialized_data,)) # 提交事务 db.commit() # 关闭cursor和连接 cursor.close() db.close() ``` 请注意,上述代码中的数据库连接信息(如主机名、用户名、密码和数据库名)需要根据实际情况替换为正确的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值