TinyXml
TinyXml结构解析
TinyXML是一个简单便捷的,基于C++的XML文件解析器,可以方便的集成到其他项目里。整个项目的类及其继承关系如下图:
- TiXmlAttribute: 一个属性是一个 名称-值 的键值对表示
- TiXmlBase: TinyXML里所有类的基类
- TIXmlComment: 注释类
- TiXmlDeclaration: XML文件的第一行
- TiXmlDocument: 通常是*节点
- TiXmlElement: 一个元素是一个容器类
- TiXmlHandle: 包装了节点指针
- TiXmlNode: DOM模型的父类
- TiXmlPrinter: 输出到内存
- TiXmlText: XML文本
- TiXmlUnknown: 任何TinyXML识别不了的都存在Unknown
- TiXmlVisitor: 实现了访问者模式的接口
关于TinyXML的使用,可以使用STL标准模板库,也可以不使用。当使用STL时,TinyXML会使用std::string,并且支持流。许多API函数拥有const char* 与 std::string两种形式。当不使用STL时,API函数均使用 const char*。
当使用STL时,加上下面的预处理命令即可:
TIXML_USE_STL
一个简单的例子,如有一个文件"demo.xml":
<?xml version="1.0" standalone=no>
<!-- Our to do list data -->
<ToDo>
<Item priority="1"> Go to the <bold>Toy store!</bold></Item>
<Item priority="2"> Do bills</Item>
</ToDo>
我们想读取解析上述文件,这样做即可:
TiXmlDocument doc( "demo.xml" );
doc.LoadFile();
文件第一行是声明(declaration),会转化成 TiXmlDeclaration 类。它也是文档节点(document node)的第一个子节点(child)。
第二行:
<!-- Our to do list data -->
是一个注释,是一个TiXmlComment对象。
<ToDo>
”ToDo“ 标签定义了一个TiXmlElement对象。例子中此对象没有包含任何属性,但它包含有两个其他的元素。
<Item priority="1">
这句话创建了另一个 TiXmlElement,它是”ToDo“元素的一个”孩子“(child)。次元素有一个属性,名字是”priority“,值为”1“。
Go to the
是一个TiXmlText对象,是叶子节点,不能包含任何节点。它是”Item“这个的TiXmlElement元素的”孩子“。
<bold>
是另一个TiXmlElement对象,也是”Item“的孩子。
总体来看对象树,其层次及结构如下:
TiXmlDocument "demo.xml"
TiXmlDeclaration "version='1.0'" "standalone=no"
TiXmlComment " Our to do list data"
TiXmlElement "ToDo"
TiXmlElement "Item" Attribtutes: priority = 1
TiXmlText "Go to the "
TiXmlElement "bold"
TiXmlText "Toy store!"
TiXmlElement "Item" Attributes: priority=2
TiXmlText "Do bills"
与C++对象间的转换实例
这个例子模拟了存储在XML文件中的程序应用的配置的载入与保存。
配置文件的内容与下面内容类似:
<?xml version="1.0" ?>
<MyApp>
<!-- Settings for MyApp -->
<Messages>
<Welcome>Welcome to MyApp</Welcome>
<Farewell>Thank you for using MyApp</Farewell>
</Messages>
<Windows>
<Window name="MainFrame" x="5" y="15" w="400" h="250" />
</Windows>
<Connection ip="192.168.0.1" timeout="123.456000" />
</MyApp>
建立对象的类
以下面基础的类开始:
#include <string>
#include <map>
#include <list>
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);
// just to show how to do it
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;
}
};
主函数:
int main(void)
{
// block: customise and save 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");
}
// block: load 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;
}
将C++状态编码成XML
保存save()函数如下:
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 );
// block: 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 );
}
}
// block: 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);
}
}
// block: 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);
}
执行完,项目目录下,会生成一个"appsettings2.xml"文件,里面保存了上面的设置。
将XML文件解码
载入函数:
void AppSettings::load(const char* pFilename)
{
TiXmlDocument doc(pFilename);
if (!doc.LoadFile()) return;
TiXmlHandle hDoc(&doc);
TiXmlElement* pElem;
TiXmlHandle hRoot(0);
// block: name
{
pElem=hDoc.FirstChildElement().Element();
// should always have a valid root but handle gracefully if it does
if (!pElem) return;
m_name=pElem->Value();
// save this for later
hRoot=TiXmlHandle(pElem);
}
// block: string table
{
m_messages.clear(); // trash existing 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;
}
}
}
// block: windows
{
m_windows.clear(); // trash existing list
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); // If this fails, original value is left as-is
pWindowNode->QueryIntAttribute("y", &w.y);
pWindowNode->QueryIntAttribute("w", &w.w);
pWindowNode->QueryIntAttribute("hh", &w.h);
m_windows.push_back(w);
}
}
// block: connection
{
pElem=hRoot.FirstChild("Connection").Element();
if (pElem)
{
m_connection.ip=pElem->Attribute("ip");
pElem->QueryDoubleAttribute("timeout",&m_connection.timeout);
}
}
}
读出的结果为:
HitchHikerApp: Don't Panic
HitchHikerApp: Show window 'BookFrame' at 15,25 (300 x 100)
HitchHikerApp: Thanks for all the fish
并注意,在载入函数里用到了 TiXmlHandle类,这是非常有用的包装的指针类,尤其适合查找节点。
更多内容请参考 TinyXml Documentation.