序列化:序列化(Serialization)库教程

1. 个非常简单情形
输出档案(archive)类似于输出数据流(stream)数据能通过<< 或 & 操作符存储到档案(archive)中:

ar << data;

ar & data;



输入档案(archive)类似于输入数据流(stream)数据能通过>> 或 & 操作符从档案(archive)中装载

ar >> data;

ar & data;



对于原始数据类型当这些操作时候数据是简单“被存储/被装载” “到/从” 档案(archive)对于类()数据类型serialize 对上面操作每个serialize 用来“存储/装载”其数据成员这个处理采用递归方式直到所有包含在类中数据“被存储/被装载”



个非常简单情形

通常用serialize 来存储和装载类数据成员

这个库包含个叫 demo.cpp 用于介绍如何用这个库下面我们从这个demo摘录代码来介绍这个库应用最简单情形

# <fstream>

// headers that implement a archive in simple text format

# <boost/archive/text_oarchive.hpp>

# <boost/archive/text_iarchive.hpp>

/////////////////////////////////////////////////////////////

// gps coordinate

//

// illustrates serialization for a simple type

//

gps_position

{

private:

friend boost::serialization::access;

// When the Archive corresponds to an output archive, the

// & operator is d similar to <<. Likewise, when the Archive

// is a type of input archive the & operator is d similar to >>.

// 这个模板序列化时被boost库

template< Archive>

void serialize(Archive & ar, const unsigned version)

{

ar & degrees;

ar & minutes;

ar & seconds;

}

degrees;

minutes;

float seconds;

public:

gps_position{};

gps_position( d, m, float s) :

degrees(d), minutes(m), seconds(s)

{}

};





{

// create and open a character archive for output

std::ofstream ofs("filename");

boost::archive::text_oarchive oa(ofs);

// create instance

const gps_position g(35, 59, 24.567f);

// write instance to archive

oa << g;

// close archive

ofs.close;



// ... some time later restore the instance to its orginal state

// create and open an archive for input

std::stream s("filename", std::ios::binary);

boost::archive::text_iarchive ia(s);

// read state from archive

gps_position g;

ia >> g;

// close archive

s.close;

0;

}



对于每个通过序列化“被存储”必须存在去实现“存储”其所有状态数据对于每个通过序列化“被装载”必须存在来实现“装载”其所有状态数据在上面例子中这些是模板成员serialize



2. 非侵入版本
在上例是侵入设计类是需要由其例子来序列化来改变这在某些情形是困难个等价可选设计如下:



# <boost/archive/text_oarchive.hpp>

# <boost/archive/text_iarchive.hpp>

gps_position

{

public:

degrees;

minutes;

float seconds;

gps_position{};

gps_position( d, m, float s) :

degrees(d), minutes(m), seconds(s)

{}

};



// 注意命名空间

boost {

serialization {

template< Archive>

void serialize(Archive & ar, gps_position & g, const unsigned version)

{

ar & g.degrees;

ar & g.minutes;

ar & g.seconds;

}

} // serialization

} // boost

这种情况生成serialize 不是gps_position类成员这有异曲同工的妙



非侵入序列化主要应用在不改变类定义就可实现类序列化为实现这种可能类必须提供足够信息来更新类状态在这个例子中我们假设类有public成员仅当提供足够信息来存储和装载才能不改变类自身在外部来序列化类状态

3. 可序列化成员
个可序列化可拥有可序列化成员例如:

bus_stop

{

friend boost::serialization::access;

template< Archive>

void serialize(Archive & ar, const unsigned version)

{

ar & latitude;

ar & longitude;

}

gps_position latitude;

gps_position longitude;

protected:

bus_stop(const gps_position & lat_, const gps_position & long_) :

latitude(lat_), longitude(long_)

{}

public:

bus_stop{}

// See item # 14 in Effective C by Scott Meyers.

// re non-virtual destructors in base es.

virtual ~bus_stop{}

};

这里类类型成员被序列化恰如原始类型被序列化

注意类bus_stop例子“存储”时其归档(archive)操作符将latitude 和 longitudeserialize 这将依次定义在gps_position中serialize 来被“存储”这种手法中通过bus_stop归档(archive)操作符,整个数据结构被存储,bus_stop是它根条目

4. 派生类
派生类应包含其基类序列化

# <boost/serialization/base_object.hpp>

bus_stop_corner : public bus_stop

{

friend boost::serialization::access;

template< Archive>

void serialize(Archive & ar, const unsigned version)

{

// serialize base information

// 基类序列化

ar & boost::serialization::base_object<bus_stop>(*this);

ar & street1;

ar & street2;

}

std:: street1;

std:: street2;

virtual std:: description const

{

street1 + " and " + street2;

}

public:

bus_stop_corner{}

bus_stop_corner(const gps_position & lat_, const gps_position & long_,

const std:: & s1_, const std:: & s2_

) :

bus_stop(lat_, long_), street1(s1_), street2(s2_)

{}

};

注意在派生类中不要直接其基类序列化这样做看似工作实际上绕过跟踪例子用于存储来消除冗余代码它也绕过写到档案中类版本信息代码 因此总是声明serialize 作为私有声明friend boost::serialization::access 将运行序列化库存取私有变量和 

5. 指针
假设我们定义了bus route包含组bus stops假定:

我们可以有几种bus stop类型(记住bus_stop是个基类)

个所给 bus_stop可以展现多于路线

个bus route 用组指向bus_stop指针来描述是方便

bus_route

{

friend boost::serialization::access;

bus_stop * stops[10];

template< Archive>

void serialize(Archive & ar, const unsigned version)

{

i;

for(i = 0; i < 10; i)

ar & stops[i];

}

public:

bus_route{}

};

stops 每个成员将被序列化但是记住每个成员是个指针 - 实际含义是什么? 序列化整个对象是要求在另个地方和时间重新构造原始数据结构用指针为了完成这些存储指针值是不够指针指向对象必须存储当成员最后被装载个新对象被创建指针被装载到类成员中

所有这切是由序列化库自动完成通过指针关联对象上述代码能完成存储和装载

6.
事实上上述方案比较复杂序列化库能检测出被序列化对象是将产生上述等价代码因此上述代码能更短写为:

bus_route

{

friend boost::serialization::access;

bus_stop * stops[10];

template< Archive>

void serialize(Archive & ar, const unsigned version)

{

ar & stops;

}

public:

bus_route{}

};

7. STL容器
上面例子用成员更多如此个应用用STL容器为如此序列化库包含为所有STL容器序列化代码因此下种方案正如我们所预期样子工作

# <boost/serialization/list.hpp>

bus_route

{

friend boost::serialization::access;

std::list<bus_stop *> stops;

template< Archive>

void serialize(Archive & ar, const unsigned version)

{

ar & stops;

}

public:

bus_route{}

};

8. 类版本
假设我们对bus_route类满意在产品中使用它段时间后发觉bus_route 类需要包含线路驾驶员名字因此新版本如下:

# <boost/serialization/list.hpp>

# <boost/serialization/.hpp>

bus_route

{

friend boost::serialization::access;

std::list<bus_stop *> stops;

std:: driver_name;

template< Archive>

void serialize(Archive & ar, const unsigned version)

{

ar & driver_name;

ar & stops;

}

public:

bus_route{}

};

完毕!异常...会发生在读取旧版本所生成数据文件时如何考虑版本问题?

通常序列化库为每个被序列化类在档案中存储版本号缺省值是0当档案装载时存储版本号可被读出上述代码可修改如下:

# <boost/serialization/list.hpp>

# <boost/serialization/.hpp>

# <boost/serialization/version.hpp>

bus_route

{

friend boost::serialization::access;

std::list<bus_stop *> stops;

std:: driver_name;

template< Archive>

void serialize(Archive & ar, const unsigned version)

{

// only save/load driver_name for er archives

(version > 0)

ar & driver_name;

ar & stops;

}

public:

bus_route{}

};

BOOST_CLASS_VERSION(bus_route, 1)

对每个类通过应用版本没有必要维护个版本文件个文件版本是所有它组成版本联合系统允许和以前版本创建档案向下兼容

9. 把serialize拆分成save/load
serialize是简单简洁并且保证类成员按同样顺序(序列化系统key)被存储/被装载可是有像这里例子装载和存储不情形例如个类有多个版本情况发生上述情形能重写为:

# <boost/serialization/list.hpp>

# <boost/serialization/.hpp>

# <boost/serialization/version.hpp>

# <boost/serialization/split_member.hpp>

bus_route

{

friend boost::serialization::access;

std::list<bus_stop *> stops;

std:: driver_name;

template< Archive>

void save(Archive & ar, const unsigned version) const

{

// note, version is always the latest when saving

ar & driver_name;

ar & stops;

}

template< Archive>

void load(Archive & ar, const unsigned version)

{

(version > 0)

ar & driver_name;

ar & stops;

}

BOOST_SERIALIZATION_SPLIT_MEMBER

public:

bus_route{}

};

BOOST_CLASS_VERSION(bus_route, 1)



BOOST_SERIALIZATION_SPLIT_MEMBER 宏生成 save 或 load代码依赖于是否档案被用于“存储”或“装载”

10. 档案
我们这里讨论将聚焦到类序列化能力上被序列化数据实际编码实现于档案(archive)类中被序列化数据流是所选档案(archive)类序列化产物(键)key设计决定这两个组件独立性允许任何序列化规范标准可用于任何档案(archive)

 

在这篇指南中我们用了个档案类-用于存储text_oarchive和用于装载text_iarchive类在库中其他档案类接口完全旦类序列化已经被定义类能被序列化到任何档案类型(例如 序列化到XML中)

假如当前档案集不能提供某个属性格式或行为需要特化应用要么创建个新要么从已有里面衍生将在后继文档中描述 



注意我们例子save和load数据在这是为了讨论方便而已通常被装载档案或许在或许不在同



T完整演示 - demo.cpp 包括:



创建各种类别 stops, routes 和 schedules

显示它

序列化到个名叫 "testfile.txt"文件中

还原到另个结构中

显示被存储结构

这个输出 分证实了对序列化系统所有要求都在这个系统中体现了对序列化文件是ASCII文本档案文件内容 能被显示
Tags:  .net序列化 对象序列化 java序列化 序列化

延伸阅读

最新评论

发表评论