[C++]使用TinyXml讀寫Xml

  • 41312
  • 0
  • C++
  • 2012-01-04

[C++]使用TinyXml讀寫Xml

在C++讀寫XML並不像在.NET一般容易,常看到的方法若不是自己解析,就是用MSXml或是TinyXml下去處理,這邊簡單的紀錄一下TinyXml的用法。

 

自網站下載完TinyXml後解壓縮後,我們可以看到裡面會有tinystr.cpp、tinystr.h、tinyxml.cpp、tinyxml.h、tinyxmlerror.cpp、與tinyxmlparser.cpp這六個檔案,TinyXml主要就是用這六個檔案。

image

 

將這六個檔案加入專案中,並在cpp檔前加入#include "stdafx",將Precompile header加入 。

image

 

在要使用到TinyXml的地方加入tinyxml.h與tinystr.h兩個必要的標頭檔,就可以開始使用了。

#include "tinyxml.h"   
#include "tinystr.h"

 

TinyXml內含TiXmlBase、TiXmlNode、TiXmlDocument、TiXmlElement、TiXmlComment、TiXmlDeclaration、TiXmlText、TiXmlUnknown、TiXmlAttribute這幾個重要的類別,從命名就可以大致了解他們的用途,像是TiXmlDocument表示Xml文件、TiXmlElement是Xml元素節點、TiXmlComment是Xml中的註解、TiXmlAttribute是Xml節點的屬性。

 

在寫入Xml時,首先必須建立TiXmlDocument,為其加入TiXmlElement、TiXmlText與其它的Xml節點元素,再叫用SaveFile方法帶入Xml檔案位置就可以了。

void SaveXml(Person* person, string file)
{	
	TiXmlDocument xmlDoc;

	TiXmlNode* rootElement = xmlDoc.InsertEndChild(TiXmlElement("Person"));

	rootElement
		->InsertEndChild(TiXmlElement("Name"))
		->InsertEndChild(TiXmlText(person->m_sName.c_str()));

	rootElement
		->InsertEndChild(TiXmlElement("NickName"))
		->InsertEndChild(TiXmlText(person->m_sNickName.c_str()));

	char buffer[256];
	_itoa(person->m_nAge, buffer,10);
	rootElement
		->InsertEndChild(TiXmlElement("Age"))
		->InsertEndChild(TiXmlText(buffer));

	xmlDoc.SaveFile(file.c_str());
}

 

讀取Xml時,一樣是要先建立TiXmlDocument物件,在建構時帶入Xml檔案位置,再叫用LoadFile將檔案讀入。讀入Xml後若讀取有問題,我們可叫用ErrorId判斷是否有錯誤發生,若取得的ErrorId大於0,可再叫用ErrorDesc去判斷發生的錯誤是甚麼。若Xml讀取沒問題的話可接著解析Xml的內容,透過RootElement可取得Xml的根節點,FirstChildElement可取得Xml子節點,NextSibling可用來取得Xml的兄弟節點,取得了Xml節點後透過GetText方法就可以得到Xml節點的內容。像是下面這樣:

void LoadXml(string file, Person* person)
{	
	TiXmlDocument xmlDoc(file.c_str());

	xmlDoc.LoadFile();

	if(xmlDoc.ErrorId() > 0)
		return;

	TiXmlElement* pRootElement = xmlDoc.RootElement();

	if(!pRootElement)
		return;

	TiXmlElement* pNode = NULL;
	
	pNode = pRootElement->FirstChildElement("Name");
	if(pNode)
	{
		person->m_sName = pNode->GetText();		
	}

	pNode = pRootElement->FirstChildElement("NickName");
	if(pNode)
	{
		person->m_sNickName = pNode->GetText();		
	}

	pNode = pRootElement->FirstChildElement("Age");
	if(pNode)
	{
		person->m_nAge = atoi(pNode->GetText());		
	}
}

 

完整的程式範例如下:

// ConsoleApplication11.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string>
#include "tinyxml.h"   
#include "tinystr.h"

using namespace std;

class Person
{
public:
	string	m_sName;
	string	m_sNickName;
	int		m_nAge;
};

void SaveXml(Person* person, string file)
{	
	TiXmlDocument xmlDoc;

	TiXmlNode* rootElement = xmlDoc.InsertEndChild(TiXmlElement("Person"));

	rootElement
		->InsertEndChild(TiXmlElement("Name"))
		->InsertEndChild(TiXmlText(person->m_sName.c_str()));

	rootElement
		->InsertEndChild(TiXmlElement("NickName"))
		->InsertEndChild(TiXmlText(person->m_sNickName.c_str()));

	char buffer[256];
	_itoa(person->m_nAge, buffer,10);
	rootElement
		->InsertEndChild(TiXmlElement("Age"))
		->InsertEndChild(TiXmlText(buffer));

	xmlDoc.SaveFile(file.c_str());
}

void LoadXml(string file, Person* person)
{	
	TiXmlDocument xmlDoc(file.c_str());

	xmlDoc.LoadFile();

	if(xmlDoc.ErrorId() > 0)
		return;

	TiXmlElement* pRootElement = xmlDoc.RootElement();

	if(!pRootElement)
		return;

	TiXmlElement* pNode = NULL;
	
	pNode = pRootElement->FirstChildElement("Name");
	if(pNode)
	{
		person->m_sName = pNode->GetText();		
	}

	pNode = pRootElement->FirstChildElement("NickName");
	if(pNode)
	{
		person->m_sNickName = pNode->GetText();		
	}

	pNode = pRootElement->FirstChildElement("Age");
	if(pNode)
	{
		person->m_nAge = atoi(pNode->GetText());		
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	string file = "Person.xml";

	Person Larry;
	Larry.m_sName		= "Larry";
	Larry.m_sNickName	= "蹂躪";
	Larry.m_nAge		= 30;
	
	SaveXml(&Larry, file);


	Person NewLarry;

	LoadXml(file, &NewLarry);

	printf("Name: %s\r\n", NewLarry.m_sName.c_str());
	printf("NickName: %s\r\n", NewLarry.m_sNickName.c_str());
	printf("Age: %d\r\n", NewLarry.m_nAge);

	return 0;
}

 

範例運行後會產生個Xml,內容像下面這般:

image

 

Xml的資料也能正常的解析。

image

 

TinyXml使用上算是比較不複雜的,但是卻有個很麻煩的問題,就是轉碼的動作必須自己處理,若不經轉換可能會有亂碼的問題,網路上有些文章就是專門在討論這樣的問題。另外,它的Xml存檔格式是用ANSI的格是下去儲存,若是要以UTF-8去儲存,必須修改tinyxml的程式碼,直接搜尋程式找到useMicrosoftBOM,將這值設為true就可以了。

image

 

這邊筆者為了方便使用,有將TinyXml包成類似.NET的XmlWriter類別,存取Xml的部分就會變得像下面這樣:

void SaveXml(Person* person, string file)
{
	XmlWriter xw(file);

	xw
		.WriteStartElement("Person")
		    .WriteElementValue("Name", person->m_sName)
		    .WriteElementValue("NickName", person->m_sNickName)
		    .WriteElementValue("Age", person->m_nAge)
		.WriteEndElement()
		.Close();
}

 

程式碼分享於下方,有需要的自行取用。

XmlWriter.h

#pragma once
#include <stack>
#include <string>
#include "tinyxml.h"   
#include "tinystr.h"

using namespace std;
class XmlWriter
{
#pragma region Const
private:
#define DEFAULT_BUFFER_SIZE 512
#pragma endregion


#pragma region Var
private:
	char				m_cBuffer[DEFAULT_BUFFER_SIZE];
	string				_sFile;
	TiXmlDocument		_XmlDoc;	
	stack<TiXmlNode*>	_StartNodes;
#pragma endregion


#pragma region Private Property
private:
	__declspec(property(get=Get_sFile,put=Set_sFile))
		string m_sFile;

	__declspec(property(get=Get_XmlDoc,put=Set_XmlDoc))
		TiXmlDocument& m_XmlDoc;

	__declspec(property(get=Get_StartNodes))
		stack<TiXmlNode*>& m_StartNodes;

	__declspec(property(get=Get_pCurrentNode,put=Set_pCurrentNode))
		TiXmlNode* m_pCurrentNode;
#pragma endregion


#pragma region Constructor & DeConstructor
public:
	XmlWriter(string file);
	~XmlWriter(void); 
#pragma endregion


#pragma region Property Process Method
private:
	inline string Get_sFile()
	{
		return _sFile;
	}

	inline void Set_sFile(string value)
	{
		if(_sFile == value)
			return;

		_sFile = value;
	}

	inline TiXmlDocument& Get_XmlDoc()
	{
		return _XmlDoc;
	}

	inline void Set_XmlDoc(TiXmlDocument& value)
	{
		_XmlDoc = value;
	}

	inline TiXmlNode* Get_pCurrentNode()
	{		
		if(m_StartNodes.empty())
		{
			return &m_XmlDoc;
		}
		return m_StartNodes.top();
	}

	inline stack<TiXmlNode*>& Get_StartNodes()
	{
		return _StartNodes;
	}
#pragma endregion


#pragma region Protected Method
protected:
	void Reset();
#pragma endregion


#pragma region Public Method
public:
	XmlWriter& WriteStartElement(string localName);
	XmlWriter& WriteEndElement();
	XmlWriter& WriteElementValue(string localName, const char* value);
	XmlWriter& WriteElementValue(string localName, string value);
	XmlWriter& WriteElementValue(string localName, int value);
	void Close();
#pragma endregion
};

 

XmlWriter.cpp

#include "stdafx.h"
#include "XmlWriter.h"

#pragma region Constructor & DeConstructor
XmlWriter::XmlWriter(string file)
{
	Reset();
	m_sFile = file;
}


XmlWriter::~XmlWriter(void)
{
	Reset();
} 
#pragma endregion



#pragma region Protected Method
void XmlWriter::Reset()
{
	_sFile			= "";
	_XmlDoc.Clear();
}
#pragma endregion


#pragma region Public Method
XmlWriter& XmlWriter::WriteStartElement(string localName)
{	
	m_StartNodes.push(m_pCurrentNode->InsertEndChild(TiXmlElement(localName.c_str())));

	return *this;
}

XmlWriter& XmlWriter::WriteEndElement()
{
	if(!m_StartNodes.empty())
		m_StartNodes.pop();

	return *this;
}

XmlWriter& XmlWriter::WriteElementValue(string localName, const char* value)
{
	m_pCurrentNode
		->InsertEndChild(TiXmlElement(localName.c_str()))
		->InsertEndChild(TiXmlText(value));

	return *this;
}

XmlWriter& XmlWriter::WriteElementValue(string localName, string value)
{
	return WriteElementValue(localName, value.c_str());
}

XmlWriter& XmlWriter::WriteElementValue(string localName, int value)
{
	_itoa(value, m_cBuffer,10);
	return WriteElementValue(localName, m_cBuffer);
}

void XmlWriter::Close()
{
	m_XmlDoc.SaveFile(m_sFile.c_str());
	Reset();
}
#pragma endregion

 

Link