C++模板编写的序列化框架
- 格式:doc
- 大小:45.00 KB
- 文档页数:4
C语言的数据序列化数据结构的序列化是个很有用的东西。
这几天在修改原来的资源管理模块,碰到从前做的几个数据文件解析的子模块,改得很烦,就重新思考序列化的方案了。
Java 和 .Net 等,由于有完整的数据元信息,语言便提供了完善的序列化解决方案。
C++ 对此在语言设计上有所缺陷,所以并没有特别好的,被所有人接受的方案。
现存的 C++ serialization 方案多类似于 MFC 在二十年前的做法。
而后,boost 提供了一个看起来更完备的方案( boost.serialization )。
所谓更完备,我指的是非侵入。
boost 的解决方案用起来感觉更现代,看起来更漂亮。
给人一种“不需要修改已有的C++ 代码,就能把本不支持serialize 的类加上这个特性”的心理快感。
换句话说,就是这件事情我能做的,至于真正做的事情会碰到什么,那就不得而知了。
好吧,老实说,我不喜欢用大量苦力(或是高智慧的结晶?)堆积起来的代码。
不管是别人出的力,还是我自己出的。
另外,我希望有一个 C 的解决方案,而不是 C++ 的。
所以从昨天开始,就抽了点时间来搞定这件事。
问题来至于 C/C++ 语言未提供类型元信息,那我们从根本上去解决好了。
得到元信息的方案不在于使用奇怪的宏,我这个古板的C 程序员,也用不起现代的template 技术。
其实自定义一下C 的结构描述方式(创造个小语言),写个小程序去解析一下就行了。
再用这个程序去生成 .h 文件,而供 serialization 库使用的元数据。
如果暂不考虑让别的动态语言更方便的分析序列化后的数据(留给以后再扩展),其实,需要做序列化的对象仅仅只有两个数据类型:值类型和对其它类型的引用。
对于值类型,我们可以简单的做内存拷贝,只需要知道值类型的长度。
而引用,在内存中,则是简单的指针。
序列化后则是相对数据块的偏移量。
这里使用 base 1 ,这样可以允许 0 依旧表示空引用。
c语言中如何序列化反序列化结构体在C语言中,序列化和反序列化结构体是一种将结构体数据转化为字节流并存储,以及将字节流数据转化为结构体对象的过程。
序列化,顾名思义,是将结构体数据转化为字节流的操作。
在许多应用中,我们需要将结构体数据以某种格式进行存储,以便在需要时恢复结构体对象。
序列化的过程中,我们需要将结构体中的各个成员逐个按照一定的顺序转化为字节流,并存储起来。
反序列化则是序列化的逆过程,将存储的字节流数据重新转化为结构体对象。
通过读取字节流数据,并按照之前序列化的顺序逐个恢复成员变量的值,最终得到完整的结构体对象。
为了更好地理解序列化和反序列化的实现,我们可以考虑以下例子:假设我们有一个学生的结构体类型,包含姓名、年龄和成绩三个成员变量。
我们希望将这些学生的信息序列化存储到文件中,并能够在需要时恢复成学生结构体对象。
首先,我们需要定义一个文件操作函数,用于读写字节流数据。
可以使用C语言中的文件操作函数,如fread和fwrite,来读写字节流数据。
接下来,我们定义一个函数将学生结构体序列化为字节流。
在这个函数中,我们使用fwrite函数逐个将学生结构体的成员变量写入文件,并存储到字节流数据中。
例如:```cvoid SerializeStudent(FILE* fp, Student* student) {fwrite(student->name, sizeof(char), strlen(student->name), fp);fwrite(&(student->age), sizeof(int), 1, fp);fwrite(&(student->score), sizeof(float), 1, fp);}```在上述代码中,我们使用fwrite函数按照name、age和score的顺序将学生结构体的成员变量逐个写入文件,即进行序列化操作。
对应地,我们需要定义一个函数将字节流反序列化为学生结构体对象。
一种提高代码复用的C++序列化框架设计梁白鸥(中国电子科技集团公司第十研究所,成都610036)摘要:序列化在各类软件中应用非常普遍㊂在实际工程应用中可能出现X M L㊁二进制和数据库的序列化操作,由于A N S I C++不支持序列化操作㊁硬编码通常产生大量重复代码,本文设计了一套支持X M L㊁二进制和数据库序列化的C++框架㊂该框架引入了M e s s a g e O b j e c t㊁S e r i a l i z e r㊁S t r e a m三个概念,并提出利用C++宏来屏蔽重复实现的细节㊂该框架在实际工程应用中大大减少了代码量,提高了代码的复用㊂关键词:C++序列化框架;X M L序列化;二进制序列化;数据库序列化中图分类号:T P311.51文献标识码:AD e s i g n o f C++S e r i a l i z e F r a m e w o r k I n c r e a s i n g R e u s a b l e o f C o d e sL i a n g B a i o u(C E T C-10,C h e n g d u610036,C h i n a)A b s t r a c t:S e r i a l i z e i s i m p l e m e n t e d i n l o t s o f s o f t w a r e s.XM L s e r i a l i z e,b i n a r y s e r i a l i z e a n d d a t a b a s e s e r i a l i z e m a y b e u s e d i n r e a l p r o j e c t s.B e c a u s e t h e A N S I C++d o n t s u p p o r t t h e s e r i a l i z e o p e r a t i o n,s o t h e h a r d c o d i n g i s i n d e s c r i b a b l y c o m p l e x.I n t h e p a p e r,a s e r i a l i z e f r a m e w o r k i s d e s i g n e d t o s u r p p o r t t h e XM L,b i n a r y a n d d a t a b a s e s e r i a l i z e.T h e t h r e e c o n c e p t s a r e i n t r o d u c e d s u c h a s M e s s a g e O b j e c t,S e-r i a l i z e r,S t r e a m,a n d t a k e a d v a n t a g e o f t h e m a c r o o f C++t o h i d e t h e d e t a i l.T h e f r a m e w o r k d e c r e a s e l o t s o f c o d e s i n p r o j e c t,a n d i n-c r e a s e t h e r e u s a b l e o f c o d e s.K e y w o r d s:C++s e r i a l i z e f r a m e w o r k;XM L s e r i a l i z e;b i n a r y s e r i a l i z e;d a t a b a s e s e r i a l i z e引言在网络软件中,特别是在远程控制软件中,序列化操作非常重要㊂序列化[1]是将对象状态转换为可保持或传输格式的过程㊂与序列化相对的是反序列化,它将流转换为对象㊂这两个过程结合起来,可以轻松地实现存储和传输数据㊂在实际工程中,数据的流向非常复杂,图1显示了其中可能出现的状况:端机A从S Q L S e r v e r中读取数据反序列化成对象,然后将该对象序列化为二进制流通过以太网发送到端机B;端机B将接收到的数据反序列化为对象,然后将该对象序列化为X M L文件,通过H T T P上传到端机C上;端机C将X M L文件反序列化为对象,再将该对象序列化到M y S Q L数据库中㊂图1实际工程的数据流向以上过程非常复杂,且可能平台异构,实现这样的操作,将耗费大量的重复性劳动㊂当将C++作为软件编程语言时,由于A N S I C++并不支持序列化操作,因而用C++实现非常复杂且具有动态特性的协议时,将变成非常枯燥而且繁琐的硬编码工作㊂因此,本文设计一套自动化的C++序列化框架,提供非常简单的接口,可大大减少工程中的编程复杂性㊂1框架的设计1.1基本设计思想在软件系统中,不管是X M L数据[2]㊁二进制数据,还是数据库数据,最终都会以一种数据结构表现出来㊂数据结构通常以结构体㊁类,以及类之间的组合或继承来表示㊂在C++软件系统中,由于结构体几乎完全等同于类,因此可以通过类及类的组合和继承来表现各种数据结构㊂该框架抽象了以下三种概念:①M e s s a g e O b j e c t:存放消息具体含义的数据结构称为M e s s a g e O b j e c t(消息对象),该对象中包含各种类型的字段,X M L㊁二进制和数据库的序列化都使用同一个M e s s a g e O b je c t㊂图3 二进制序列化框架的类结构②S e r i a l i z e r:真正实现二进制㊁X M L 和数据库序列化过程的类称为S e r i a l i z e r (序列化器),框架提供了二进制序列化器B i n a r y S e r i a l i z e r ㊁T i n yX m l 序列化器T i n y X m l S e r i a l i z e r (使用T i n y X m l 解析器)㊁X e r c e s 序列化器X e r c e s X m l S e -r i a l i z e r (使用X e r c e s 解析器)㊁数据库序列化器D a t a b a s e S e r i a l i z e r㊂③S t r e a m :每一个M e s s a g e O b je c t 可能会存入到二进制文件B i n a r y-F i l e S t r e a m ㊁二进制内存B i n a r yM e m o -r y S t r e a m ㊁X M L 字符串X m l S t r i n g-S t r e a m ㊁X M L 文件X m l F i l e S t r e a m ㊁压缩的X M L 字符串X m l C o m pr e s s e d S -t r i n g S t r e a m ㊁压缩的X M L 文件X m l C o m p r e s s e d -F i l e S t r e a m 中,M y S Q L 数据库㊁S Q L S e r v e r 2000数据库等目标㊂这些不同的目标称为S t r e a m (流)㊂图2 序列化器㊁流㊁消息对象的关系在该框架中,将M e s s a -g e O b je c t ㊁S e r i a l i z e r ㊁S t r e a m 有效组织在一起,形成如图2所示的关系㊂序列化时,M e s s a -g e O b j e c t 通过S e r i a l i z e r 保存(s t o r e )到S t r e a m 中;反序列化时,S e r i a l i z e r 从S t r e a m 中读取(l o a d )数据,重新构建M e s s a g e O b je c t ㊂C ++的基本数据类型有多种,如c h a r ㊁u n s i gn e d c h a r ㊁s h o r t ㊁u n s i g n e d s h o r t ㊁i n t ㊁u n s i g n e d i n t ㊁l o n g ㊁u n s i g n e d l o n g㊁f l o a t ㊁d o u b l e ㊁b o o l ㊁数组以及指针等,同时S T L 中经常会使用s t r i n g 和v e c t o r ㊁l i s t 等容器㊂为了可以将上面提到的每种数据都能序列化,就必须在S e r i a l i z e r 中提供每种类型的序列化方法㊂框架中使用C ++T e m p l a t e 减少了重复的代码㊂为了能嵌套地使用M e s s a g e O b je c t ,必须提供一个公共的接口,这样就可以在对象中包含另外一个或者多个对象㊂为了使得每个类具有序列化成不同方式的操作,就需要分别为X M L ㊁二进制和数据库的序列化提供一个统一的接口㊂1.2 框架的设计基于以上基本思想,该框架实现了上述的操作㊂通过UM L [3]简单描述了该框架二进制序列化部分的整体架构,X M L 和数据库序列化部分的类结构和二进制序列化的结构基本相同,如图3所示,该框架使用了桥接模式[4],将S t r e a m 和M e s s a g e O b je c t 的抽象和实现部分隔离开来,使得它们可以独立变化㊂如果需要将一个对象序列化为二进制数据,则必须继承B i n a r y M e s s a g e O b j e c t ,同时实现v i r t u a l b o o l B i n a r yS e r -i a l i z e (B i n a r y S e r i a l i z e r *p B i n a r yS e r i a l i z e r )=0;纯虚函数,在子类中对每个变量进行序列化㊂如果需要将一个对象序列化为X M L 数据,则必须继承X m l M e s s a g e O b je c t ,同时实现v i r t u a l X M L _E L E -M E N T*X m l S e r i a l i z e (X m l S e r i a l i z e r*p X m l S e r i a l i z e r,X M L _E L E M E N T*p P a r e n t X m l E l e m e n t )=0;纯虚函数,在子类中对每个变量进行序列化㊂当定义好M e s s a g e O b je c t 之后,使用者只需要定义相应的S t r e a m ,并调用S t r e a m 中已经定义好的操作符<<进行序列化,操作符>>进行反序列化㊂需要注意的是,在对二进制进行序列化时,不同C P U的字节顺序不一样,如X 86为小端字节序,而P o w e r P C 为大端字节序,更有甚者对字节内的位序也有区分,因此框架指定了S t r e a m 的字节顺序以及字节内部的位顺序,这样便能将不同C P U 所带来的异构动态适应㊂在对X M L 序列化时,考虑到X M L 描述信息量较大,在进行网络传输的时候需要进行压缩处理,在框架中使用z l i b 库对X M L 进行压缩和解压缩,因此定义了X m l C o m -p r e s s e d S t r i n g S t r e a m 和X m l C o m pr e s s e d F i l e S t r e a m 两个流,用于压缩X M L 的序列化㊂1.3 利用C ++宏屏蔽重复的细节可以看到,在进行二进制序列化时,v i r t u a l b o o l B i n a -r y S e r i a l i z e (B i n a r yS e r i a l i z e r *)虚方法中,会有大量重复性的代码,不同的可能只有数据的类型和对应的属性㊂在对整形的二进制序列化中,变化的只有字节数或者比特数㊂因此,这些重复的代码可以通过宏来实现㊂拿二进制序列化来说,提取其中几个宏的定义如下:#d e f i n e B E G I N _B I N A R Y _S E R I A L I Z E ()\v i r t u a l b o o l B i n a r y S e r i a l i z e (B i n a r y S e r i a l i z e r *p B i n a r yS e r i a l i z e r ){#d e f i n e C O MM O N _V A R _S P E C I F Y _B Y T E _N U M (v a r ,b yt e N u m )\ i f (p B i n a r y S e r i a l i z e r >I s S t o r i n g ())\ p B i n a r y S e r i a l i z e r >S e r i a l i z e V a r (v a r ,b y t e N u m );\ e l s e \p B i n a r y S e r i a l i z e r >D e s e r i a l i z e V a r (v a r ,b yt e N u m );\ //省略了其他的二进制序列化宏#d e f i n e E N D _B I N A R Y _S E R I A L I Z E ()\r e t u r n t r u e ;\}X M L 序列化也可如法炮制㊂因此,通过定义对各种变量序列化的宏,使得使用者只需要使用各种宏就可以实现各种序列化的定义㊂2 框架的使用假设有一个需求表明要从X M L 文档r e a d .x m l 反序列化为对象,同时要序列化为指定字节数目的二进制流通过网络传输;在网络的另一端,接收到二进制流后反序列化为对象,再将对象序列化为X M L 压缩的s a v e .d a t 文档,同时将X M L 字符串打印在屏幕上㊂X M L 文件如下:<R o o t ><S t u d e n t S e t N a m e >我的S t u d e n t 集合</S t u d e n t S e t N a m e > <S t u d e n t S e t n u m="2"><S t u d e n t N a m e ="小红"> <O l d >22</O l d > <H e i g h t >1.65</H e i g h t > </S t u d e n t ><S t u d e n t N a m e ="小明"> <O l d >24</O l d ><H e i g h t >1.75</H e i gh t > </S t u d e n t > </S t u d e n t S e t ></R o o t >首先需要将X M L 文档所描述的结构信息转换为类图4 用U M L 表示X M L 结构以及类与类之间的关系㊂如图4所示㊂很明显该X M L描述了有一个存放动态S t u d e n t 的集合S t u d e n t S e t,以及这个S t u d e n t S e t 集合的名字㊂因此可以创建下面两个类:c l a s s S t ude n t :p u b l i c X m l M e s s a g e O b j e c t ,p u b l i c B i n a r y M e s s a g e O b je c t {//X M L 序列化宏B E G I N _XM L _S E R I A L I Z E (S t u d e n t ) //S t u d e n t 元素XM L _A T T R I B U T E _V A R (S t u d e n t ,N a m e ,m _s t r -N a m e ) //N a m e 属性和m _s t r N a m e 绑定XM L _E L E M E N T _V A R (O l d ,m _n O l d)//O l d 元素和m _n O l d 绑定XM L _E L E M E N T _V A R (H e i g h t ,m _f H e i g h t )//H e i g h t 元素和m _f H e i gh t 绑定 E N D _XM L _S E R I A L I Z E () //二进制序列化宏 B E G I N _B I N A R Y _S E R I A L I Z E () C OMMO N _V A R (m _s t r N a m e ) //动态的s t r i n g 对象 C OMMO N _V A R _S P E C I F Y _B Y T E _N UM (m _n O l d ,2) //2个字节的m _n O l dC OMMO N _V A R (m _f H e i g h t ) //4个字节的f l o a t E ND _B I N A R Y _SE R I A L I Z E () s t d ::s t r i n g m _s t r N a m e ;//姓名 u n s i gn e d i n t m _n O l d ;//年龄 f l o a t m _f H e i gh t ;//身高};c l a s s S t ude n t S e t :p u b l i c X m l M e s s a g e O b j e c t ,p u b l i c B i n a r yM e s s a -g e O b je c t {//XM L 序列化宏 B E G I N _XM L _S E R I A L I Z E (R o o t ) XM L _E L E M E N T _V A R (S t u d e n t S e t N a m e ,m _s t r S t u -d e n t S e t N a m e) XM L _E L E M E N T _S T L _C O N T A I N E R _O B J E C T _V A R (S t u d e n t S e t ,m _S t u d e n t V e c t o r) E N D _XM L _S E R I A L I Z E () //二进制序列化宏 B E G I N _B I N A R Y _S E R I A L I Z E () C OMMO N _V A R (m _s t r S t u d e n t S e t N a m e)//动态变化的s t r i n g//动态变化的v e c t o r ,其长度用一个字节进行序列化 S T L _C O N T A I N E R _O B J E C T _V A R (m _S t u d e n t V e c -t o r ,1) E N D _B I N A R Y _S E R I A L I Z E () s t d ::s t r i n g m _s t r S t u d e n t S e t N a m e ;//S t u d e n t 集合名字 s t d ::v e c t o r <S t u d e n t >m _S t u d e n t V e c t o r ;//S t u d e n t 的集合};有了上面两个类,完成需求就非常简单,只需要几行代码就可以完成㊂在发送端的代码如下:X m l F i l e S t r e a m r e a d X m l F i l e S t r e a m ("r e a d .x m l ");//定义一个XM L 文件流S t u d e n t S e t s t u d e n t s;r e a d X m l F i l e S t r e a m>>s t u d e n t s;//从文件中反序列化构建s t u d e n t s 对象B i n a r y M e m o r yS t r e a m s e n d B i n S t r e a m ; //定义二进制内存流s e n d B i n S t r e a m<<s t u d e n t s;//将s t u d e n t s 对象序列化为二进制内存流s o c k e t .S e n d (s e n d B i n S t r e a m.G e t B u f f e r (),s e n d B i n S t r e a m.G e t -B u f f e r L e n());//将二进制流通过S O C K E T 发送出去通过网络传输将二进制流发送到另一台计算机上,从相应的S O C K E T 上接收到二进制缓冲区p B u f f e r 和长度n L e n,在接收端的代码如下:B i n a r y M e m o r y S t r e a m r e c v B i n S t r e a m (pB u f f e r ,n L e n );//将r e c v 数据放入二进制内存流中S t u d e n t s s t u d e n t s;r e c v B i n S t r e a m>>s t u d e n t s;//从二进制内存流中反序列化构建s t u d e n t s 对象X m l S t r i n g S t r e a m x m l S t r i n g;//定义XM L 字符串流x m l S t r i n g<<s t u d e n t s ;//将s t u d e n t s 对象序列化到XM L 字符串流中c o u t <<x m l S t r i n g .G e t X m l S t r i n g()<<e n d l ;//在屏幕打印XM L 字符串X m l C o m pr e s s e d F i l e S t r e a m s a v e X m l F i l e S t r e a m ("s a v e .d a t ");s a v e X m l F i l e S t r e a m<<s t u d e n t s;//将s t u d e n t s 对象序列化到压缩XM L 的s a v e .d a t 文件中可以看出,通过使用该框架,所写的代码将大大减少,且支持了各种类型的变量以及各种动态的变量类型,使得代码复用得到真正的体现㊂3 性能测试与分析基于上述S t u d e n t s 测试用例,其中S t u d e n t 对象个数为1000个,在P e n t i u m D u a l C P U 主频为1.6G H z㊁内存为512M B 的机器上进行了几个典型操作的性能测试,其中使用X e r c e s -c 的X M L 解析器㊂测试结果如表1所列㊂可以得出以下结论:从S t r e a m 到M e s s a g e O b je c t 的反序列化要比从M e s s a g e O b j e c t 到S t r e a m 的时间要长,特别是在具有包含对象的动态v e c t o r 变量的时候,由于反序列化需要创建大量对象,会消耗一定的时间;X M L 数据的压缩率很高,且压缩的耗时并不是很长,因此在网络传输X M L 时,最好进行压缩,这样可以节约很多网络带宽;二进制序列化的效率远远高于X M L 的序列化效率,因为X M L 基本上是对字符串的操作,会消耗大量的时间㊂表1 性能测试结果测试项目序列化时间/m s压缩比/%对象到二进制内存小于1 二进制内存到对象8对象到X M L 文件50 X M L 文件到对象61对象到压缩的X M L 字符串5399.46压缩的X M L 字符串到对象6399.46结 语本文设计了一个简化序列化操作的框架,该框架提出了M e s s a g e O b je c t ㊁S e r i a l i z e r 和S t r e a m 的概念,通过三个概念类的有机组合,利用C++宏屏蔽重复的细节,实现了将对象序列化为不同流的功能,支持二进制序列化㊁X M L 序列化以及数据库的序列化㊂使用该框架,代码量减少了90%以上,减少了程序员的重复劳动,提高了编码效率和质量㊂参考文献[1]L u k a s z O p y r c h a l ,A t u l P r a k a s h .E f f i c i e n t O b je c t S e r i a l i z a t i o n i n J a v a [E B /O L ].[20181213].h t t p://w w w.e e c s .u m i c h .e d u /~a p r a k a s h /p a p e r s /o p y r c h a l p r a k a s h i c d c s -w o r k s h o p 99.pd f .[2]D o n B o x ,A a r o n S k o n n a r d ,J o h n L a m.E s se n t i a l XM L [M ].H o n g k o n g :A d d i s o n W e s l e y L o n gm a n I n c ,2003.[3]J a m e s R u m b a u g h .T h e U n i f i e d M o d e l i n g L a n g u a ge R ef e r e n c e M a n u a l [M ].北京:机械工业出版社,2001.[4]E r i c h G a mm a .设计模式:可复用面向对象软件的基础[M ].刘建中,等译.北京:机械工业出版社,2000.梁白鸥,主要研究方向为分布式网络体系结构与中间件技术㊂(责任编辑:薛士然 收稿日期:2018-12-13)A r m P e l i o n 物联网平台为全面部署物联网公共设施奠定基础A r m 宣布与E D M I 合作,将M b e d O S 与P e l i o n 物联网平台整合到先进的智能仪表解决方案中,实现安全的设备连接㊁管理㊁固件升级,并为未来物联网公共设施的应用打下基础㊂A r m 与E D M I 联手一同简化并加快各种联网智能仪表以及AM I 解决方案的安全部署㊂E D M I 正着手将P e l i o n 物联网平台与M b e d O S (P S A C e r -t i f i e d)整合到当前顶尖的智能仪表解决方案中,进一步扩展已部署在全球各地的数百万个端点㊂公共事业公司可运用下一代物联网解决方案,无缝开发㊁部署㊁连接㊁以及管理数量众多的智能电表,并提高数据搜集的数量与频率,进而快速定位和解决停电问题㊁检测窃电㊁追踪使用模式,以更好地规划资产升级与维护等㊂。
c#序列化详解⽰例⼏种序列化技术:1)⼆进制序列化保持类型保真度,这对于在应⽤程序的不同调⽤之间保留对象的状态很有⽤。
例如,通过将对象序列化到剪贴板,可在不同的应⽤程序之间共享对象。
您可以将对象序列化到流、磁盘、内存和⽹络等等。
远程处理使⽤序列化“通过值”在计算机或应⽤程序域之间传递对象。
2)XML 序列化仅序列化公共属性和字段,且不保持类型保真度。
当您要提供或使⽤数据⽽不限制使⽤该数据的应⽤程序时,这⼀点是很有⽤的。
由于 XML 是⼀个开放式标准,因此,对于通过 Web 共享数据⽽⾔,这是⼀个很好的选择。
SOAP 同样是⼀个开放式标准,这使它也成为⼀个颇具吸引⼒的选择。
3)使⽤提供的数据协定,将类型实例序列化和反序列化为 XML 流或⽂档(或者JSON格式)。
常应⽤于WCF通信。
BinaryFormatter序列化可被定义为将对象的状态存储到存储媒介中的过程。
在此过程中,对象的公共字段和私有字段以及类的名称(包括包含该类的程序集)都被转换为字节流,然后写⼊数据流。
在以后反序列化该对象时,创建原始对象的精确复本。
1、使⼀个类可序列化的最简单⽅式是按如下所⽰使⽤ Serializable 属性标记。
2、有选择的序列化通过⽤ NonSerialized 属性标记成员变量,可以防⽌它们被序列化3、⾃定义序列化1) 在序列化期间和之后运⾏⾃定义⽅法最佳做法也是最简单的⽅法(在 .Net Framework 2.0 版中引⼊),就是在序列化期间和之后将下列属性应⽤于⽤于更正数据的⽅法:复制代码代码如下:OnDeserializedAttributeOnDeserializingAttributeOnSerializedAttributeOnSerializingAttribute具体事例如下:复制代码代码如下:// This is the object that will be serialized and deserialized.[Serializable()]public class TestSimpleObject{// This member is serialized and deserialized with no change.public int member1;// The value of this field is set and reset during and// after serialization.private string member2;// This field is not serialized. The OnDeserializedAttribute// is used to set the member value after serialization.[NonSerialized()]public string member3;// This field is set to null, but populated after deserialization.private string member4;// Constructor for the class.public TestSimpleObject(){member1 = 11;member2 = "Hello World!";member3 = "This is a nonserialized value";member4 = null;}public void Print(){Console.WriteLine("member1 = '{0}'", member1);Console.WriteLine("member2 = '{0}'", member2);Console.WriteLine("member3 = '{0}'", member3);Console.WriteLine("member4 = '{0}'", member4);}[OnSerializing()]internal void OnSerializingMethod(StreamingContext context){member2 = "This value went into the data file during serialization.";}[OnSerialized()]internal void OnSerializedMethod(StreamingContext context){member2 = "This value was reset after serialization.";}[OnDeserializing()]internal void OnDeserializingMethod(StreamingContext context){member3 = "This value was set during deserialization";}[OnDeserialized()]internal void OnDeserializedMethod(StreamingContext context){member4 = "This value was set after deserialization.";}}2) 实现 ISerializable 接⼝对于⽤ Serializable 属性标记且在类级别上或其构造函数上具有声明性或命令性安全的类,不应使⽤默认序列化。
C实现高效的数据序列化与反序列化数据序列化与反序列化是计算机领域中常见的操作,它们将数据对象转化为字节序列用于存储或传输,并且能够通过反序列化操作将字节序列还原为原始的数据对象。
在C语言中,实现高效的数据序列化与反序列化可以帮助我们更好地处理数据的存储和传输,提高程序的效率和性能。
本文将介绍如何在C语言中实现高效的数据序列化与反序列化的方法与技巧。
一、数据序列化1. 使用结构体存储数据在C语言中,我们可以使用结构体来存储数据,在结构体中定义各种类型的成员变量,如int、float、char等等,来表示不同的数据类型。
通过结构体,我们可以将相关的数据组织在一起,并便于进行序列化操作。
2. 将数据转化为字节序列在进行序列化时,我们需要将数据转化为字节序列。
可以使用指针对结构体进行操作,通过指针访问结构体的成员变量,并使用类型转换将其转化为字节序列。
可以使用memcpy函数将数据拷贝到目标字节序列中。
3. 存储数据长度信息为了在反序列化时知道需要还原的数据长度,我们可以在序列化时额外存储数据的长度信息。
可以在序列化字节序列的最开始位置存储一个整型变量,用来表示序列化数据的长度。
二、数据反序列化1. 读取字节序列在反序列化时,我们需要从序列化的字节序列中读取数据,并将读取的数据还原为原始的数据对象。
可以通过指针对字节序列进行操作,使用指针逐个字节地读取数据。
2. 还原数据类型根据序列化时的数据类型,我们可以还原出原始的数据类型。
可以使用类型转换将读取的字节序列转化为数据类型,并将其存储到相应的变量中。
3. 根据长度信息还原数据在序列化的字节序列中存储了数据的长度信息,我们可以根据长度信息还原出需要还原的数据。
可以根据长度信息动态地分配内存空间,并将读取的字节序列复制到新分配的内存空间中。
三、优化实现1. 数据压缩与解压缩在进行数据序列化与反序列化时,可以考虑使用压缩算法来减小数据的大小。
常用的压缩算法有LZ77、LZ78、LZW等。
利用C++模板编写的序列化框架2005-01-22 14:03作者:潘凯出处:VCHELP责任编辑:方舟简介在这个框架中包含了一个序列化的基本框架,一套基本的类型识别系统,可以识别基础类型,复杂类型,自定义类型,STD的容器类型,而且可以这个基础上进行递归的扩展。
可以将复杂的数据结构序列化到文件,并从文件中恢复。
包含了完整的自动单元测试,和测试案例,点此下载。
正文写这个序列化框架最初是想用在一个大型的项目上,在那个项目中有一些相当复杂的在运行时构建出来的树形数据结构,如果可以将这个内存树序列化起来可以大大节约下次创建的时间。
另外在自己做的一些小工具中,有些数据想保存在文件中,以后再从文件中读取,用序列化的方式也十分方便。
而且那时正好系统的学习了一下C++模板技术,感觉在一般的编程活动中很难用到一些比较高级的模板技术,所以想用C++模板技术来写这个序列化框架。
最后那个项目中没有使用这个序列化框架,但我至少达到了第二个目标,写这个序列化框架让我对C++模板技术有了更深层次的理解。
在这个框架中包含了一个序列化的基本框架,一套基本的类型识别系统,可以识别基础类型,复杂类型,自定义类型,STD的容器类型,而且可以这个基础上进行递归的扩展。
在写这个框架的同时,我也写了完整的测试案例。
如果没有测试案例,要调试这样的框架可就真是难与登天,因为模板方面的错误,编译器报出来的信息很难看,有的根本就没用。
代码是在VC7.1下写的,也只能在VC7.1下用,VC6对于C++模板的支持非常有限,而其他的编译器在这方面的支持也有出入。
如果要用于其他的编译器可能要修改部分类型识别方面的代码。
测试框架我用的是cppunit(1.9.14),这是个开源的测试框架,可以在下载到。
其中类型识别方面的代码我主要是参考了《C++ template》一书,和boost中的部分代码。
由于是用模板写的比MFC中的运行时序列化框架在效率上的表现要好得多。
C#通⽤类库整理--序列化类 程序员在编写应⽤程序的时候往往要将程序的某些数据存储在内存中,然后将其写⼊某个⽂件或是将它传输到⽹络中的另⼀台计算机上以实现通讯。
这个将程序数据转化成能被存储并传输的格式的过程被称为"序列化"(Serialization),⽽它的逆过程则可被称为"反序列化"(Deserialization)。
.NET存在⼏种默认提供的序列化,⼆进制序列化,xml和json序列化会序列化所有的实例共有属性。
本⽂主要介绍json序列化。
由于数据实体类型较多,我们使⽤泛型,⼤⼤的提⾼了代码的质量。
引⽤空间:System.Runtime.Serialization.Json;public readonly static SerializeHelper Instance = new SerializeHelper();///<summary>///将C#数据实体转化为JSON数据///</summary>///<param name="obj">要转化的数据实体</param>///<returns>JSON格式字符串</returns>public string JsonSerialize<T>(T obj){DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));MemoryStream stream = new MemoryStream();serializer.WriteObject(stream, obj);stream.Position = 0;StreamReader sr = new StreamReader(stream);string resultStr = sr.ReadToEnd();sr.Close();stream.Close();return resultStr;}///<summary>///将JSON数据转化为C#数据实体///</summary>///<param name="json">符合JSON格式的字符串</param>///<returns>T类型的对象</returns>public T JsonDeserialize<T>(string json){DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json.ToCharArray()));T obj = (T)serializer.ReadObject(ms);ms.Close();return obj;}。
序列化框架的实现原理序列化框架的实现原理主要包括以下几个步骤:1. 对象图遍历:序列化框架通过遍历对象的属性来构建对象图。
从根对象开始,递归地遍历所有引用类型的属性,直到遍历完所有可达的对象。
2. 对象图构建:在遍历对象图的过程中,序列化框架会为每个遇到的对象创建一个序列化描述符(Serialization Descriptor),用于描述对象的类型信息以及属性的名称和值。
3. 属性序列化:对于每个属性,序列化框架根据属性的类型调用相应的序列化方法,将属性的值转化为字节流。
简单类型(如基本数据类型)会直接按照字节流的形式进行序列化,而复杂类型(如自定义对象)可能需要进行进一步的递归序列化。
4. 序列化结果组装:将所有属性序列化得到的字节流组装成一个完整的字节流,用于存储对象的所有属性及其值。
5. 字节流持久化:将序列化结果的字节流持久化到存储介质中,如文件或网络传输。
可以通过文件流、Socket等方式将字节流写入到存储介质中。
反序列化的实现原理与序列化类似,主要包括以下几个步骤:1. 字节流读取:从存储介质中读取序列化结果的字节流。
2. 属性反序列化:根据字节流中的类型信息和属性值,进行属性的反序列化。
对于简单类型,直接按照字节流的形式进行反序列化;对于复杂类型,可能需要进一步的递归反序列化。
3. 对象图构建:根据属性的反序列化结果和对象图的关系,重新构建对象图。
4. 对象图还原:根据对象图,创建新的对象,并将反序列化得到的属性值设置到相应的属性上。
5. 还原对象返回:将还原后的对象返回给调用方。
总的来说,序列化框架的实现原理是通过遍历对象的属性,将属性的值转化为字节流进行持久化存储;反序列化则是通过读取字节流,将属性的值从字节流中还原出来重新构建对象图。
C#序列化结构体
在将对象或结构体序列化成⼆进制数据流时,我们通常都会使⽤ System.Runtime.Serialization.Formatters.Binary.BinaryFormatter 类来实现,
但是这种⽅式会把对象或结构体的类型信息给序列化到数据流中, 在做通迅时,这种⽅式是不可取的.
在c/c++中,通常对某个协议使⽤ struct 进⾏存储,然后将其序列化, c#也能实现这样的功能. 具体的实现办法:
public static byte[] GetBytes<TStruct>(TStruct data) where TStruct : struct
{
int structSize = Marshal.SizeOf(typeof(TStruct));
byte[] buffer = new byte[structSize];
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
Marshal.StructureToPtr(data, handle.AddrOfPinnedObject(), false);
handle.Free();
return buffer;
}
作为参数的结构体,需要使⽤[StructLayout]和[FieldOffset]分别声明结构体和结构体中的成员.通过上⾯的⽅法,可以将结构体中的成员序列化成
我们需要的⼆进制数据流. ⼤部分的需求都可以实现, 但⽬前,我还不太清楚,如果需要实现类似于c/c++中, 使⽤联合体作为结构体成员,该如果实现.
如果有⼈知道,请留⾔告知,谢谢!。
c#开发可替换的通⽤序列化库开篇继续吹⽜。
其实没有什么可吹的了,哈哈哈哈哈主要是写⼀个通⽤库,既可以直接⽤,⼜⽅便替换,我的序列化都是采⽤第三⽅的哈。
我不上完整代码了,只是给⼤家讲讲过程。
1.写⼀个序列化的类,我是使⽤前⾯给⼤家介绍的messagepack哈。
⼀个静态类,这个随⾃⼰需要。
2.写另⼀个封装类CommonSerializer,相当于⼀个模板,⾥⾯所有序列化,反序列化⽅法都是虚⽅法,调⽤前⾯的messagepack⽅法,最后⽅法名称都⼀样。
3.再写⼀个对外的序列化泛型类SerializerFactory。
⾥⾯的⽅法和上⾯的CommonSerializer⼀样,最好名称也⼀致。
这⾥: SerializerFactory<TSerializer> where TSerializer: CommonSerializer这样的定义⼤家是不是明⽩了呢,在你⾃⼰的项⽬中,SerializerFactory<CommonSerializer>.⽅法:使⽤默认的序列化当你要替换其中的⽅法或者组件时。
写⼀个myclass类继承CommonSerializer,覆盖⽅法即可。
SerializerFactory<myclass>.⽅法SerializerFactory类⾥⾯部分代码需要private static Dictionary<string, CommonSerializer> dic_Serializer = new Dictionary<string, CommonSerializer>();//采⽤属性⽅式,不使⽤⽅法,更加舒服private static CommonSerializer SerializerCls{get{//不考虑锁,替换对象⽽已,也没有遍历Type serializerType = typeof(TSerializer);CommonSerializer serializer = null;if (dic_Serializer.TryGetValue(serializerType.FullName, out serializer)){return serializer;}else{serializer = Activator.CreateInstance<TSerializer>();dic_Serializer[serializerType.FullName] = serializer;return serializer;}}}这样你可以使⽤不同的序列化组件在同⼀个库中。
利用C++模板编写的序列化框架
2005-01-22 14:03作者:潘凯出处:VCHELP责任编辑:方舟
简介
在这个框架中包含了一个序列化的基本框架,一套基本的类型识别系统,可以识别基础类型,复杂类型,自定义类型,STD的容器类型,而且可以这个基础上进行递归的扩展。
可以将复杂的数据结构序列化到文件,并从文件中恢复。
包含了完整的自动单元测试,和测试案例,点此下载。
正文
写这个序列化框架最初是想用在一个大型的项目上,在那个项目中有一些相当复杂的在运行时构建出来的树形数据结构,如果可以将这个内存树序列化起来可以大大节约下次创建的时间。
另外在自己做的一些小工具中,有些数据想保存在文件中,以后再从文件中读取,用序列化的方式也十分方便。
而且那时正好系统的学习了一下C++模板技术,感觉在一般的编程活动中很难用到一些比较高级的模板技术,所以想用C++模板技术来写这个序列化框架。
最后那个项目中没有使用这个序列化框架,但我至少达到了第二个目标,写这个序列化框架让我对C++模板技术有了更深层次的理解。
在这个框架中包含了一个序列化的基本框架,一套基本的类型识别系统,可以识别基础类型,复杂类型,自定义类型,STD的容器类型,而且可以这个基础上进行递归的扩展。
在写这个框架的同时,我也写了完整的测试案例。
如果没有测试案例,要调试这样的框架可就真是难与登天,因为模板方面的错误,编译器报出来的信息很难看,有的根本就没用。
代码是在VC7.1下写的,也只能在VC7.1下用,VC6对于C++模板的支持非常有限,而其他的编译器在这方面的支持也有出入。
如果要用于其他的编译器可能要修改部分类型识别方面的代码。
测试框架我用的是cppunit(1.9.14),这是个开源的测试框架,可以在下载到。
其中类型识别方面的代码我主要是参考了《C++ template》一书,和boost中的部分代码。
由于是用模板写的比MFC中的运行时序列化框架在效率上的表现要好得多。
使用起来也相当的简单。
如果要学习C++模板的高级技术,研究一下这个框架可以获益良多。
由于是框架代码,我写得相当规范,有注释,也有完整的测试案例,可以进行自动的回归测试。
使用的方法比较简单请参考(fileRWTest.cpp)文件中的测试案例。
普通的数据类型:
(unsigned char, unsigned short, unsigned int, unsigned long, signed char, signed short, signed int, signed
long, bool, char, wchar_t, unsigned long long, signed long long, float, double, lon g double)可以直接序列化及反序列化。
对于指针类型:
会序列化指针具体指向的对象,如果指针指向的对象的类型是序列化框架无法识别的类型会报出编译错误。
注意在反序列化时,只需要传一个空指针即可,序列化框架会将被序列化的对象的值反序列化到堆上,并将地址付给指针。
如果传一个有值的指针,在DEBUG模式下会在运行时引发一个断言错误。
在RELEASE下会导致原来指针指向的对象被泄漏。
对于普通数据类型的数组:
会将整个数组以内存拷贝的方式序列化到内存,即使没被真正赋值的元素。
反序列化时传一个相同类型的数组即可。
需要注意的是,传进的数组的容量必须大于或等于被序列化的数组的容量,否则会引发数组越界的内存错误,在DEBUG模式下,会引发一个断言错误。
非普通数据类型的数组:
数组元素的类型可以是除普通数据类型之外的所有被序列化框架所支持的类型。
序列化时会针对每一个元素调用序列化框架对它的具体序列化特化,反序列化时亦然。
由于在RELEASE模式下类类型的数组在申明后,编译器会生成调用相应类的缺省构造函数的代码。
但对于原始类型,如指针数组类型如果不显式的手工初始化,数组中的值是无意的随机值。
这种情况序列化框架无法识别,会赞成严重的内存错误。
另对于指针数组的某些元素为NULL的情况,序列化框架也无法处理,在DEBUG模式下会引发一个断言错误。
因些如果是指针数组除非数组中的元素全部为有意义的指针,否则不应该做为一个数组来序列化,而应该加入相应的遍历逻辑,将有意思的元素逐个序列化。
对于一般的数组,如果有意思的只是其中的少部分元素,也应该以上述方式进行序列化,以提高性能。
自定义数据类类型:
不需要拷贝构造函数,不需要拷贝赋值函数,不需要析构函数的类。
如老式的struct结构类型。
这种类型可以通过直接拷贝内存而被高效的序列化及化序列化。
只需要让一个类从_data_class_tag派生,序列化框架就会将它当成普通的数据类类型处理。
自定义复杂类型:
对于非数据类类型,必须从CSerializable派生,关在类的定义中加入SERIALIZABLE(name, x)宏,name是该类的名字,x是相应的版本号。
版本号的引入主要是避免在一个类被修改后,和以前生成的序列化文件一起使用,以免引起内存错误。
在类中还必须实现virtual bool Serialize(CMedia *) const;函数,在该函数中写具体的序列化代码。
该函数的内容很简单,按序列化及反序列化用为两段,简单的为每一个需要序列化及反序列化的成员函数调用即可,如下列:
if (pMedia->IsStoring()) {
*pMedia <<m_1 <<m_2 <<m_3 <<m_4 <<m_5;
return true;
}
if (pMedia->IsLoading()) {
*pMedia >>m_1 >>m_2 >>m_3 >>m_4 >>m_5;
return true;
}
注意序列化和反序列化的顺序不要错。
std::string及std::wstring类型:
使用比较简单。
值得注意的是和将字符串数组做字符指针用的情况一样。
如果申明了一个容量很大的string(一般是为了避免在追加时的内存重分配开销),却只用了一小部分。
序列化并反序列化,string对象的容量只是刚好有内存的那部分。
std::pair类型:
只要是pair的first和second必须是序列化框架所支持的类型就可以被正常的序列化及反序列化。
std容器类型:
(vector,list,deque,stack,queue,set,multiset,map,multimap)
支持以上的容器类型,其中容器中的元素类型必须是序列化框架所支持的类型。