XML与C++对象的相互转化
介绍
这个例子假设你在用一个XML文件来加载和保存你的应用程序配置,举例来说,有点像example4.xml。
有许多方法可以做到这点。例如,看看TinyBind项目:http://sourceforge.net/projects/tinybind
这一节展示了一种普通老式的方法来使用XML加载和保存一个基本的对象结构。
建立你的对象类
从一些像这样的基本类开始:
#include
<string
>
#include
<map>
using
namespace
std
;
typedef
std
::map<std
::string
,std
::string
> MessageMap;
// 基本的窗口抽象 - 仅仅是个示例
class
WindowSettings
{
public
:
int
x,y,w,h;
string
name;
WindowSettings()
: x(0), y(0), w(100), h(100), name("Untitled"
)
{
}
WindowSettings(int
x, int
y, int
w, int
h, const string
& name)
{
this
->x=x;
this
->y=y;
this
->w=w;
this
->h=h;
this
->name=name;
}
};
class
ConnectionSettings
{
public
:
string
ip
;
double
timeout;
};
class
AppSettings
{
public
:
string
m_name;
MessageMap m_messages;
list
<WindowSettings> m_windows;
ConnectionSettings m_connection;
AppSettings() {}
void
save(const char
* pFilename);
void
load(const char
* pFilename);
// 仅用于显示它是如何工作的
void
setDemoValues()
{
m_name="MyApp"
;
m_messages.clear();
m_messages["Welcome"
]="Welcome to "
+m_name;
m_messages["Farewell"
]="Thank you for using "
+m_name;
m_windows.clear();
m_windows.push_back(WindowSettings(15,15,400,250,"Main"
));
m_connection.ip="Unknown"
;
m_connection.timeout=123.456;
}
};
这是一个基本的mian(),它向我们展示了怎样创建一个默认的settings对象树,怎样保存并再次加载:
int
main(void
)
{
AppSettings settings;
settings.save("appsettings2.xml"
);
settings.load("appsettings2.xml"
);
return
0;
}
接下来的main()展示了如何创建,修改,保存和加载一个settings结构:
int
main(void
)
{
// 区块:定制并保存settings
{
AppSettings settings;
settings.m_name="HitchHikerApp"
;
settings.m_messages["Welcome"
]="Don’t Panic"
;
settings.m_messages["Farewell"
]="Thanks for all the fish"
;
settings.m_windows.push_back(WindowSettings(15,25,300,250,"BookFrame"
));
settings.m_connection.ip="192.168.0.77"
;
settings.m_connection.timeout=42.0;
settings.save("appsettings2.xml"
);
}
// 区块:加载settings
{
AppSettings settings;
settings.load("appsettings2.xml"
);
printf("%s: %s/n"
, settings.m_name.c_str(),
settings.m_messages["Welcome"
].c_str());
WindowSettings & w=settings.m_windows.front();
printf("%s: Show window ’%s’ at %d,%d (%d x %d)/n"
,
settings.m_name.c_str(), w.name.c_str(), w.x, w.y, w.w, w.h);
printf("%s: %s/n"
, settings.m_name.c_str(),
settings.m_messages["Farewell"
].c_str());
}
return
0;
}
当save()和load()完成后(请看下面),运行这个main()就会在控制台看到:
HitchHikerApp: Don’t Panic
HitchHikerApp: Show window ‘BookFrame’ at 15,25 (300 x 100)
HitchHikerApp: Thanks for all the fish
把C++状态编码成XML
有很多方法能够做到把文档对象保存到文件中,这就是其中一个:
void
AppSettings::save(const char
* pFilename)
{
TiXmlDocument doc;
TiXmlElement* msg;
TiXmlComment * comment;
string
s;
TiXmlDeclaration* decl = new
TiXmlDeclaration( "1.0"
, ""
, ""
);
doc.LinkEndChild( decl );
TiXmlElement * root = new
TiXmlElement(m_name.c_str());
doc.LinkEndChild( root );
comment = new
TiXmlComment();
s=" Settings for "
+m_name+" "
;
comment->SetValue(s.c_str());
root->LinkEndChild( comment );
// 区块:messages
{
MessageMap::iterator iter;
TiXmlElement * msgs = new
TiXmlElement( "Messages"
);
root->LinkEndChild( msgs );
for
(iter=m_messages.begin(); iter != m_messages.end(); iter++)
{
const string
& key=(*iter).first;
const string
& value=(*iter).second;
msg = new
TiXmlElement(key.c_str());
msg->LinkEndChild( new
TiXmlText(value.c_str()));
msgs->LinkEndChild( msg );
}
}
// 区块:windows
{
TiXmlElement * windowsNode = new
TiXmlElement( "Windows"
);
root->LinkEndChild( windowsNode );
list
<WindowSettings>::iterator iter;
for
(iter=m_windows.begin(); iter != m_windows.end(); iter++)
{
const WindowSettings& w=*iter;
TiXmlElement * window;
window = new
TiXmlElement( "Window"
);
windowsNode->LinkEndChild( window );
window->SetAttribute("name"
, w.name.c_str());
window->SetAttribute("x"
, w.x);
window->SetAttribute("y"
, w.y);
window->SetAttribute("w"
, w.w);
window->SetAttribute("h"
, w.h);
}
}
// 区块:connection
{
TiXmlElement * cxn = new
TiXmlElement( "Connection"
);
root->LinkEndChild( cxn );
cxn->SetAttribute("ip"
, m_connection.ip.c_str());
cxn->SetDoubleAttribute("timeout"
, m_connection.timeout);
}
doc.SaveFile(pFilename);
}
用修改过的main运行会生成这个文件:
<?xml version="1.0" ?>
<HitchHikerApp>
<!– Settings for HitchHikerApp –>
<Messages>
<Farewell>Thanks for all the fish</Farewell>
<Welcome>Don't Panic</Welcome>
</Messages>
<Windows>
<Window name="BookFrame" x="15" y="25" w="300" h="250" />
</Windows>
<Connection ip="192.168.0.77" timeout="42.000000" />
</HitchHikerApp>
从XML中解码出状态
就像编码一样,也有许多方法可以让你从自己的C++对象结构中解码出XML。下面的方法使用了TiXmlHandles。
void
AppSettings::load(const char
* pFilename)
{
TiXmlDocument doc(pFilename);
if
(!doc.LoadFile()) return
;
TiXmlHandle hDoc(&doc);
TiXmlElement* pElem;
TiXmlHandle hRoot(0);
// 区块:name
{
pElem=hDoc.FirstChildElement().Element();
// 必须有一个合法的根结点,如果没有则温文地处理(译注:直接返回
)
if
(!pElem) return
;
m_name=pElem->Value();
// 保存起来以备后面之用
hRoot=TiXmlHandle(pElem);
}
// 区块:string table
{
m_messages.clear(); // 清空已有的table
pElem=hRoot.FirstChild( "Messages"
).FirstChild().Element();
for
( pElem; pElem; pElem=pElem->NextSiblingElement())
{
const char
*pKey=pElem->Value();
const char
*pText=pElem->GetText();
if
(pKey && pText)
{
m_messages[pKey]=pText;
}
}
}
// 区块:windows
{
m_windows.clear(); // 清空链表
TiXmlElement* pWindowNode=hRoot.FirstChild( "Windows"
)
.FirstChild().Element();
for
( pWindowNode; pWindowNode;
pWindowNode=pWindowNode->NextSiblingElement())
{
WindowSettings w;
const char
*pName=pWindowNode->Attribute("name"
);
if
(pName) w.name=pName;
pWindowNode->QueryIntAttribute("x"
, &w.x); // 如果失败,原值保持现状
pWindowNode->QueryIntAttribute("y"
, &w.y);
pWindowNode->QueryIntAttribute("w"
, &w.w);
pWindowNode->QueryIntAttribute("hh"
, &w.h);
m_windows.push_back(w);
}
}
// 区块:connection
{
pElem=hRoot.FirstChild("Connection"
).Element();
if
(pElem)
{
m_connection.ip=pElem->Attribute("ip"
);
pElem->QueryDoubleAttribute("timeout"
,&m_connection.timeout);
}
}
}