现在 , 越来越多的程序使用 xml 文件作为应用程序的配置文件 , 在 windows 平台上 , 一般的程序使用微软的 msxml 接口来读写 xml 文件 .
xml 文件原则上可以存储任意形式的数据 , 比如象点坐标 , 颜色 rgb 值 , 矩形坐标 , 字符串 , 数值或者其他自己定义的数据结构等 . 如果 xml 需要存储结构化的数据列 , 或者存储在 xml 文件中的数据因为逻辑或者业务的原因存在结构上的嵌套包含关系 , 比如要存储某个窗口的位置 , 标题 , 坐标等情况 , 还要存储这个窗口的子窗口的相同信息 .
在情况下 , 会存在下列几个问题
1. 应用程序配置结构和 xml 读写操作过于耦合 , 如果要将不同的结构写入 xml 文件 , 需要操作 msxml 调用不同的接口 , 而一旦这些配置结构发生改变 , 将需要同时修改 xml 文件的读写代码 . 对于不同的配置结构 , 就需要写不同的 xml 读写操作 , 代码重用性不高 .
2. 对于多个数据对象和有嵌套包含关系的数据结构而言 , 还需要自己实现循环语句对 xml 文件中的对象进行读写 .
本文实现了一个 xml 读写和自身结构无关的类模板 , 并且支持结构嵌套 , 简化了 xml 的读写操作 , 需要 Loki for vc6 库支持 .
如想要存储一组 web 站点的结构 , 包括 url(string), 端口 (int), 用户名 (string), 口令 (string)
可以定义下面的类结构 :
class __app_config : public Xml_Attribute_List <TYPELIST_1( XML_ATTR_NULLTYPE )>
{
public :
__app_config () {
SetTagName ( _T ( "AppConfig" ));
}
~ __app_config () {}
};
typedef Xml_Config_Node < __app_config > AppConfigRoot ;
class __site_info : public
Xml_Attribute_List <TYPELIST_4( XML_ATTR_CSTRING , XML_ATTR_INT , XML_ATTR_CSTRING , XML_ATTR_CSTRING )>
{
public :
__site_info () {
SetTagName ( _T ( "WebSite" ));
SetAttributeName ( _T ( "url" ), _T ( "port" ), _T ( "username" ), _T ( "password" ));
}
~ __site_info () {}
GET_SET_ATTR4 ( url , port ,username,password, TYPELIST_4( XML_ATTR_CSTRING , XML_ATTR_INT , XML_ATTR_CSTRING , XML_ATTR_CSTRING ))
};
typedef Xml_Config_Node < __site_info > WebSiteInfoNode ;
typedef XmlConfigNodeFactory <TYPELIST_2( AppConfigRoot , WebSiteInfoNode )> NodeCreateFactory ;
typedef XmlConfigFile < AppConfigRoot , NodeCreateFactory > AppConfigFileBase ;
class AppConfigFile : public AppConfigFileBase
{
public :
void AddSiteInfo ( LPCTSTR url , int port , LPCTSTR user , LPCTSTR pwd ) {
WebSiteInfoNode * pnode = new WebSiteInfoNode ;
pnode -> GetAttribute ().Set_url( url );
pnode -> GetAttribute (). Set_port ( port );
pnode -> GetAttribute (). Set_username ( user );
pnode -> GetAttribute (). Set_password ( pwd );
GetRoot (). AddChildNode ( pnode );
}
int GetSiteInfoCount () {
return GetRoot (). GetChildCount ();
}
WebSiteInfoNode * GetSiteInfo ( int index ) {
Xml_NodeData_Base * pbase = GetRoot (). GetChildNode ( index );
if ( pbase ) {
WebSiteInfoNode * pnode = ( WebSiteInfoNode *) pbase ;
return pnode ;
} else {
return NULL ;
}
}
public :
AppConfigFile () {}
~ AppConfigFile () {}
};
配置信息存储和加载的代码
void SaveConfig ()
{
AppConfigFile lconfigfile ;
lconfigfile . AddSiteInfo ( "192.168.0.1" , 80 , "guest" , "hello" );
lconfigfile . AddSiteInfo ( "192.168.0.2" , 80 , "guest" , "hello" );
lconfigfile . AddSiteInfo ( "192.168.0.3" , 80 , "guest" , "hello" );
lconfigfile . AddSiteInfo ( "192.168.0.4" , 80 , "test" , "hi" );
lconfigfile . SaveToXmlFile ( "c:\\test.xml" );
}
void output_config ( WebSiteInfoNode * pnode )
{
if ( pnode ) {
cout << ( LPCTSTR ) pnode -> GetAttribute ().Get_url() << " " ;
cout << pnode -> GetAttribute (). Get_port () << " " ;
cout << ( LPCTSTR ) pnode -> GetAttribute (). Get_username () << " " ;
cout << ( LPCTSTR ) pnode -> GetAttribute (). Get_password () << endl ;
}
}
void LoadConfig ()
{
AppConfigFile lconfigfile ;
lconfigfile . LoadFromXmlFile ( "c:\\test.xml" );
int i , count ;
count = lconfigfile . GetSiteInfoCount ();
if ( count > 0 ) {
for ( i = 0 ; i < count ; i ++) {
WebSiteInfoNode * pnode = lconfigfile . GetSiteInfo ( i );
output_config ( pnode );
}
}
}
可以看见,在代码中,没有具体的有关xml文件的读写操作的,涉及的只是于业务相关的数据结构的定义和存取操作,同时,在业务需求发生变化或者需要添加新的业务结构时,只需要修改业务结构定义和定义新的业务结构并将其加入节点创建工厂就可以了.
代码下载
http://www.cppblog.com/Files/hdqqq/xml_config_file.rar
附加说明 :
代码中使用抽象工厂作来创建节点对象 , 对于存储于 xml 文件中的结构 , 采用 md5 的 hash 值作为每个结构的 id.