XML序列化数据对象(三) --- 实现
一. 实现界面
1.1 大致界面
图1
1.2 类类型
图2
1.3 变量输入
图3
二. 使用步骤
2.1 添加一些类
图4
2.2 添加一些变量
图5
2.3 可以保存类型和变量为xml文件
添加了类型和变量后, 这些数据可以保存为xml文件(例如aa.xml), 也可以重新加载这个XML文件, 方便以后修改使用.
假设CTest类的结构变化了, 那么序列化/反序列化的代码也就需要改变了, 你可以重新加载aa.xml对CTest的结构修改, 然后再次生成序列化/反序列化代码即可.
aa.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>
2.4 生成序列化/反序列化的代码
#pragma once
// 这里include你自己的头文件
// 序列化
class CHYSerialization
{
public:
CHYSerialization();
~CHYSerialization();
bool SerializationOpen(const std::string& strFileName);
bool SerializationClose();
// 基本类型的序列化
bool SerializationClassType_int(CHYMLItem* pCHYMLItemParent, const std::string& strObjectName, const int& nValue);
bool SerializationClassType_long(CHYMLItem* pCHYMLItemParent, const std::string& strObjectName, const long& lValue);
bool SerializationClassType_double(CHYMLItem* pCHYMLItemParent, const std::string& strObjectName, const double& dValue);
bool SerializationClassType_float(CHYMLItem* pCHYMLItemParent, const std::string& strObjectName, const float& fValue);
bool SerializationClassType_char(CHYMLItem* pCHYMLItemParent, const std::string& strObjectName, const char& chValue);
bool SerializationClassType_string(CHYMLItem* pCHYMLItemParent, const std::string& strObjectName, const std::string& strValue);
// 用户定义的类/结构体等类型的序列化
bool SerializationClassType_CTest(CHYMLItem* pCHYMLItemParent, const std::string& strObjectName, const CTest& refValue);
bool SerializationClassType_IntVector(CHYMLItem* pCHYMLItemParent, const std::string& strObjectName, const IntVector& refValue);
// 变量的序列化函数
bool SerializationObject_m_nA(const int& refValue);
bool SerializationObject_m_dB(const double& refValue);
bool SerializationObject_m_intVector(const IntVector& refValue);
private:
CHYML m_hyML; // 你使用的XML解析库相关变量
CHYMLParser m_hyMLParser;
CHYMLItem* m_pCHYMLItemParent;
std::string m_strFileName;
};
// 反序列化
class CHYReSerialization
...
// Cpp文件代码很长
...
以上这些代码都是自动生成的, 随类型结构的变化而变化.
以后要序列化某类型的变量, 可以简单的生成这些代码即可.
把h文件和cpp文件加入到你的工程即可.
2.5 序列化/反序列化的代码的使用
变量m_nA, m_dB, m_intVector本身就是你的工程里面需要才添加到代码生成器的.
序列化:
// 你工程里的变量是这样的
int m_nA = 5;
double m_dB = 5.9;
IntVector m_intVector;
m_intVector.push_back(1);
m_intVector.push_back(3);
m_intVector.push_back(4);
m_intVector.push_back(7);
....
// 序列化
CHYSerialization hySerialization;
hySerialization.SerializationOpen("C:\ss.xml"); // 变量的序列化信息保存在ss.xml文件中
// 序列化这3个变量
hySerialization.SerializationObject_m_nA(m_nA);
hySerialization.SerializationObject_m_dB(m_dB);
hySerialization.SerializationObject_m_intVector(m_intVector);
hySerialization.SerializationClose();
生成的变量的序列化信息ss.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>
反序列化:
CHYReSerialization hyReSerialization;
hyReSerialization.ReSerializationOpen("C:\ss.xml");
hyReSerialization.ReSerializationObject_m_nA(m_nA);
hyReSerialization.ReSerializationObject_m_dB(m_dB);
hyReSerialization.ReSerializationObject_m_intVector(m_intVector);
hyReSerialization.ReSerializationClose();
初步使用了一下, 效果还可以.
三. 总结
3.1 这样自动生成序列化/反序列化的代码使用起来的确比较方便, 但是也有繁琐的地方, 例如生成代码后, 还要把h文件和cpp文件拷贝到自己的工程, 还要在h文件中添加相关的头文件(你的类头文件). 对比boost库, 只需要些相关的模板代码代码就可以了.反正就是没有boost库方便.
3.2 添加类类型的处理非常非常的繁琐和乱, 因为类型很多(basetype, stltype, struct, class, stltemplate, typedef), 还有很多判断条件, 这些判断我是通过界面交互来处理的, 可能还有很多没有考虑到的地方. 可惜自己不懂C++的语法分析, 通过语法分析, 就很容易分析出这些类型, 而且更强大.
3.3 设计成类似boost库那样序列化的操作方式是不是更好?
3.4 用代码写代码需要在更高一层上想问题. 平时写代码, 你直接把代码写入对应的cpp/h文件, 然后编译, 看是否有语法错误, 编译通过了, 就运行看有没有运行上/逻辑上的错误;用代码写代码还要加多一层, 就是你先确保生成代码的程序(代码生成器)编译运行没错, 然后编译生成器生成的代码, 假设这代码有语法错误, 你需要返回到生成器工程对应的字符地方修改这些字符, 如果生成的代码语法没有但运行有错, 那还要分析你所设计的生成代码是否有问题. 这种过程是不断的在生成器代码和被生成代码之间切换, 编译, 生成, 拷贝, 编译..... (有时候会很乱!)
3.5 通过序列化/反序列化代码生成器, 体会到用代码写代码跟C++的模板编程(泛化编程)有与曲同工之妙! 必须在更高一层处想问题.当然泛化编程更规范, 更标准. (看这文章).
3.6 C++代码生成器生成代码, 生成的代码要编译后才能用, 这是因为C++的语言特性决定的. 假设我有一个代码生成器, 生成的代码不需要编译, 能直接使用, 那多好啊. 脚本语言可以(看这文章).