介绍
这个例子假设你在用一个XML文件来加载和保存你的应用程序配置,举例来说,有点像example4.xml。
有许多方法可以做到这点。例如,看看TinyBind项目:http://sourceforge.net/projects/tinybind
这一节展示了一种普通老式的方法来使用XML加载和保存一个基本的对象结构。
建立你的对象类
从一些像这样的基本类开始:
#include<string>
#include<map>
usingnamespacestd;
typedefstd::map<std::string,std::string>MessageMap;
//基本的窗口抽象-仅仅是个示例
classWindowSettings
{
public:
intx,y,w,h;
stringname;
WindowSettings()
:x(0),y(0),w(100),h(100),name("Untitled")
{
}
WindowSettings(intx,inty,intw,inth,conststring&name)
{
this->x=x;
this->y=y;
this->w=w;
this->h=h;
this->name=name;
}
};
classConnectionSettings
{
public:
stringip;
doubletimeout;
};
classAppSettings
{
public:
stringm_name;
MessageMapm_messages;
list<WindowSettings>m_windows;
ConnectionSettingsm_connection;
AppSettings(){}
voidsave(constchar*pFilename);
voidload(constchar*pFilename);
//仅用于显示它是如何工作的
voidsetDemoValues()
{
m_name="MyApp";
m_messages.clear();
m_messages["Welcome"]="Welcometo"+m_name;
m_messages["Farewell"]="Thankyouforusing"+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对象树,怎样保存并再次加载:
intmain(void)
{
AppSettingssettings;
settings.save("appsettings2.xml");
settings.load("appsettings2.xml");
return0;
}
接下来的main()展示了如何创建,修改,保存和加载一个settings结构:
intmain(void)
{
//区块:定制并保存settings
{
AppSettingssettings;
settings.m_name="HitchHikerApp";
settings.m_messages["Welcome"]="Don’tPanic";
settings.m_messages["Farewell"]="Thanksforallthefish";
settings.m_windows.push_back(WindowSettings(15,25,300,"BookFrame"));
settings.m_connection.ip="192.168.0.77";
settings.m_connection.timeout=42.0;
settings.save("appsettings2.xml");
}
//区块:加载settings
{
AppSettingssettings;
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:Showwindow’%s’at%d,%d(%dx%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_messages["Farewell"].c_str());
}
return0;
}
当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
voidAppSettings::save(constchar*pFilename)
{
TiXmlDocumentdoc;
TiXmlElement*msg;
TiXmlComment*comment;
strings;
TiXmlDeclaration*decl=newTiXmlDeclaration("1.0","","");
doc.LinkEndChild(decl);
TiXmlElement*root=newTiXmlElement(m_name.c_str());
doc.LinkEndChild(root);
comment=newTiXmlComment();
s="Settingsfor"+m_name+"";
comment->SetValue(s.c_str());
root->LinkEndChild(comment);
//区块:messages
{
MessageMap::iteratoriter;
TiXmlElement*msgs=newTiXmlElement("Messages");
root->LinkEndChild(msgs);
for(iter=m_messages.begin();iter!=m_messages.end();iter++)
{
conststring&key=(*iter).first;
conststring&value=(*iter).second;
msg=newTiXmlElement(key.c_str());
msg->LinkEndChild(newTiXmlText(value.c_str()));
msgs->LinkEndChild(msg);
}
}
//区块:windows
{
TiXmlElement*windowsNode=newTiXmlElement("Windows");
root->LinkEndChild(windowsNode);
list<WindowSettings>::iteratoriter;
for(iter=m_windows.begin();iter!=m_windows.end();iter++)
{
constWindowSettings&w=*iter;
TiXmlElement*window;
window=newTiXmlElement("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=newTiXmlElement("Connection");
root->LinkEndChild(cxn);
cxn->SetAttribute("ip",m_connection.ip.c_str());
cxn->SetDoubleAttribute("timeout",m_connection.timeout);
}
doc.SaveFile(pFilename);
}
<?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。
voidAppSettings::load(constchar*pFilename)
{
TiXmlDocumentdoc(pFilename);
if(!doc.LoadFile())return;
TiXmlHandlehDoc(&doc);
TiXmlElement*pElem;
TiXmlHandlehRoot(0);
//区块:name
{
pElem=hDoc.FirstChildElement().Element();
//必须有一个合法的根结点,如果没有则温文地处理(译注:直接返回)
if(!pElem)return;
m_name=pElem->Value();
//保存起来以备后面之用
hRoot=TiXmlHandle(pElem);
}
//区块:stringtable
{
m_messages.clear();//清空已有的table
pElem=hRoot.FirstChild("Messages").FirstChild().Element();
for(pElem;pElem;pElem=pElem->NextSiblingElement())
{
constchar*pKey=pElem->Value();
constchar*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())
{
WindowSettingsw;
constchar*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);
}
}
}
dump_to_stdout的完整列表
下面是一个可直接运行的示例程序,使用上面提到过的递归遍历方式,可用来加载任意的XML文件并把结构输出到STDOUT上。
//指南示例程序
#include"stdafx.h"
#include"tinyxml.h"
//———————————————————————-
//STDOUT输出和缩进实用函数
//———————————————————————-
constunsignedintNUM_INDENTS_PER_SPACE=2;
constchar*getIndent(unsignedintnumIndents)
{
staticconstchar*pINDENT="+";
staticconstunsignedintLENGTH=strlen(pINDENT);
unsignedintn=numIndents*NUM_INDENTS_PER_SPACE;
if(n>LENGTH)n=LENGTH;
return&pINDENT[LENGTH-n];
}
//与getIndent相同,但最后没有“+”
constchar*getIndentAlt(unsignedintnumIndents)
{
staticconstchar*pINDENT="";
staticconstunsignedintLENGTH=strlen(pINDENT);
unsignedintn=numIndents*NUM_INDENTS_PER_SPACE;
if(n>LENGTH)n=LENGTH;
return&pINDENT[LENGTH-n];
}
intdump_attribs_to_stdout(TiXmlElement*pElement,unsignedintindent)
{
if(!pElement)return0;
TiXmlAttribute*pAttrib=pElement->FirstAttribute();
inti=0;
intival;
doubledval;
constchar*pIndent=getIndent(indent);
printf("\n");
while(pAttrib)
{
printf("%s%s:value=[%s]",pIndent,pAttrib->Name(),pAttrib->Value());
if(pAttrib->QueryIntValue(&ival)==TIXML_SUCCESS)printf("int=%d",ival);
if(pAttrib->QueryDoubleValue(&dval)==TIXML_SUCCESS)printf("d=%1.1f",dval);
printf("\n");
i++;
pAttrib=pAttrib->Next();
}
returni;
}
voiddump_to_stdout(TiXmlNode*pParent,unsignedintindent=0)
{
if(!pParent)return;
TiXmlNode*pChild;
TiXmlText*pText;
intt=pParent->Type();
printf("%s",getIndent(indent));
intnum;
switch(t)
{
caseTiXmlNode::DOCUMENT:
printf("Document");
break;
caseTiXmlNode::ELEMENT:
printf("Element[%s]",pParent->Value());
num=dump_attribs_to_stdout(pParent->ToElement(),indent+1);
switch(num)
{
case0:printf("(Noattributes)");break;
case1:printf("%s1attribute",getIndentAlt(indent));break;
default:printf("%s%dattributes",getIndentAlt(indent),num);break;
}
break;
caseTiXmlNode::COMMENT:
printf("Comment:[%s]",pParent->Value());
break;
caseTiXmlNode::UNKNOWN:
printf("Unknown");
break;
caseTiXmlNode::TEXT:
pText=pParent->ToText();
printf("Text:[%s]",pText->Value());
break;
caseTiXmlNode::DECLARATION:
printf("Declaration");
break;
default:
break;
}
printf("\n");
for(pChild=pParent->FirstChild();pChild!=0;pChild=pChild->NextSibling())
{
dump_to_stdout(pChild,indent+1);
}
}
//加载指定的文件并把它的结构输出到STDOUT上
voiddump_to_stdout(constchar*pFilename)
{
TiXmlDocumentdoc(pFilename);
boolloadOkay=doc.LoadFile();
if(loadOkay)
{
printf("\n%s:\n",pFilename);
dump_to_stdout(&doc);
}
else
{
printf("Failedtoloadfile\"%s\”\n",pFilename);
}
}
//———————————————————————-
//main(),打印出从命令行指定的文件
//———————————————————————-
intmain(intargc,char*argv[])
{
for(inti=1;i<argc;i++)
{
dump_to_stdout(argv[i]);
}
return0;
}
从命令行或者DOS窗口运行它,例如:
C:\dev\tinyxml> Debug\tinyxml_1.exe example1.xml example1.xml: Document + Declaration + Element [Hello] (No attributes) + Text: [World]