TinyXml
- TinyXml結構解析
- 與C++對象間的轉換執行個體
-
- 建立對象的類
- 主函數:
- 将C++狀态編碼成XML
- 将XML檔案解碼
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)。
第二行:
是一個注釋,是一個TiXmlComment對象。
”ToDo“ 标簽定義了一個TiXmlElement對象。例子中此對象沒有包含任何屬性,但它包含有兩個其他的元素。
這句話建立了另一個 TiXmlElement,它是”ToDo“元素的一個”孩子“(child)。次元素有一個屬性,名字是”priority“,值為”1“。
Go to the
是一個TiXmlText對象,是葉子節點,不能包含任何節點。它是”Item“這個的TiXmlElement元素的”孩子“。
是另一個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.