XML序列化数据对象(二) --- 原理和构思
一. 定义数据对象的XML标签结构
例如:
<ObjectSerialization>
<m_nA class = "int">
5
</m_nA>
<m_dB class = "double">
5.900000
</m_dB>
<m_intVector class = "IntVector">
<vectorItem0 class = "int">
1
</vectorItem0>
<vectorItem1 class = "int">
3
</vectorItem1>
<vectorItem2 class = "int">
4
</vectorItem2>
<vectorItem3 class = "int">
7
</vectorItem3>
</m_intVector>
</ObjectSerialization>
根标签: ObjectSerialization
第2级标签: 变量名 例如m_nA
标签的内容: 变量值 例如标签m_nA的内容是5, 那么变量m_nA的值就是5
标签属性:
class: 变量的类型(类名), 例如 int, double, IntVector
class的值有: int, double, long, char, std::string, 使用typedef定义的类型, 使用typedef定义的stl容器类型(std::vector, std::map, std::list, std::set)
二. 变量的序列化与反序列化.
使用XML序列化变量过程就是写入XML过程, 反序列化过程就是读取XML过程.
(每一个XML解析器的调用流程可能有区别).
现在有
int m_nA = 0;
int m_nB = 1;
和
struct CTest
{
double m_dC;
double m_dD;
};
CTest m_test;
// 序列化/反序列化的伪代码
Write_int();
Write_double();
Write_m_nA()
{
Write_int(m_nA);
}
Write_m_nB()
{
Write_int(m_nB);
}
Write_CTest()
{
Write_double(m_dC);
Write_double(m_dD);
}
三. 序列化/反序列化的代码需要跟着这些变量结构变化的
现在CTest结构改变为
struct CTest
{
double m_dC;
int m_nD;
};
// 伪代码
Write_CTest()
{
Write_double(m_dC);
Write_int(m_nD);
}
变量类型结构的变化, 序列化/反序列化的代码也会跟需要变化, 这种变化在一些复杂的数据机构中, 代码修改起来是很繁琐和重复的, 很容易出错. 但是这种变化又是有规律的. 既然有规律, 就可以用代码写一个程序去固定它和规范它.
在MFC中就是使用宏来把这种变化规律来固定的.
在boost中是使用模板和宏来把这种变化规律来固定的.
我这里则写了一个程序自动生成变量的序列化/反序列化代码来固定.
也就是说变量的类型结构变化了, 程序将根据类型结构的变化自动生成该变量的序列化/反序列化的代码.
(这里说的非常绕口啊)
概括说一句就是: 我写一个小程序, 该小程序能根据变量的类型结构自动生成变量的序列化/反序列化的代码.
(就是用代码来写代码, 或者说在代码之上写代码, 这个程序就是一个代码生成器).
四. 实现原理和过程
4.1 生成变量的序列化/反序列化代码所需要的信息
从点一中的变量存储在XML中的结构知道需要的信息有:
A. 变量名.
B. 变量类型的结构
C. XML的操作代码.
有了这3类信息, 即可自动生成变量的序列化/反序列化代码.
其中B.变量类型的结构
4.2 变量类型的结构
从点一中的变量存储在XML中的内容知道, 最终写入XML标签树的树叶部分的是数字和字符. 标签之间的结构则反映类型结构.
所以变量类型的结构分类有:
A. 基本类型: char, int, double, long, float, std::string (我这里把std::string作为基本类型, 也就是字符, 因为我std::string使用的非常多).
B. struct和class: 也就是结构体和类, 结构体和类的成员层层下去, 最终也是基本类型.
C. typedef定义的类型: typedef定义A和typedef定义B, 最终也是基本类型.
D. stl定义的容器: 有std::vector, std::list; std::map; std::set(我用得最多也就这几种). 最终也是基本类型.
有了这些类型结构后, 如何保存一个变量就很清晰了, 那么生成变量的序列化/反序列化代码也就很清晰了.
代码生成器通过变量类型结构生成序列化/反序列化代码.
4.3 变量类型的结构的表示和描述
用XML描述如下:
<structure_def>
<class name = "CTest">
<include file = "a" sys = "0">
</include>
<include file = "b" sys = "1">
</include>
</class>
<basetype name = "int">
</basetype>
<basetype name = "double">
</basetype>
<basetype name = "long">
</basetype>
<stltype name = "std::vector" typenamecount = "1">
</stltype>
<stltemplate beptr = "0" beptr1 = "0" name = "IntVector" type = "std::vector" typename1 = "int">
</stltemplate>
<ClassObject>
<m_nA class = "int">
</m_nA>
<m_dB class = "double">
</m_dB>
<m_intVector class = "IntVector">
</m_intVector>
</ClassObject>
</structure_def>
说明:
根标签名称:structure_def:
类型标签:
basetype(主要有char, int, double, long, float, std::string这些类型)
class(用户定义的类)
struct(用户定义的结构体)
stltype(主要有std::vector, std::list, std::map, std::set)
typedef(使用typedef定义的类型)
stltemplate(使用typedef定义的stltype, 例如typedef std::vector<int> IntVector)
类型标签的属性:
name(类型名称, 基本类型, stl的一些类型, 用户定义的类/结构体等的名称, 例如int, double, CTest, STTest, IntVector等)
typenamecount(stltype类型中模板参数的个数, 例如std::vector模板参数个数是1, std::map模板参数个数是2)
beptr(表示该类型是否为指针类型, 0为否, 1为是)
beptr1(表示stltemplate类型中第一个模板参数是否为指针类型, 0为否, 1为是)
beptr2(表示stltemplate类型中第二个模板参数是否为指针类型, 0为否, 1为是, 如果有的话)
type(表示stltemplate类型中的stltype类型的类名称)
typename1(表示stltemplate类型中第一个模板参数类型的类名称)
typename2(表示stltemplate类型中第二个模板参数类型的类名称)
变量标签: ClassObject(用于记录需要序列化的变量名称和类型), ClassObject的子项就是使用变量名的标签, 变量名标签的class属性代表变量类型.
4.4 通过类类型信息生成变量的序列化/反序列化代码
代码生成器提供用户定义各种类的操作和定义各种变量的操作, 有了这些数据, 代码生成器即可生成变量的序列化/反序列化的代码. 用户把代码拷贝到自己的工程作简单的配置修改, 编译即可.