天天看點

[C++]TinyXmlTinyXml結構解析與C++對象間的轉換執行個體

TinyXml

  • TinyXml結構解析
  • 與C++對象間的轉換執行個體
    • 建立對象的類
    • 主函數:
    • 将C++狀态編碼成XML
    • 将XML檔案解碼

TinyXml結構解析

TinyXML是一個簡單便捷的,基于C++的XML檔案解析器,可以友善的內建到其他項目裡。整個項目的類及其繼承關系如下圖:

[C++]TinyXmlTinyXml結構解析與C++對象間的轉換執行個體
  • 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.

繼續閱讀