TinyXML快速入門(一)(二)(三)

masikkk發表於2013-11-05

Visual C++ TinyXml快速入門(一)

xml檔案本質就是小型的資料庫,換個角度來說就是,你對資料庫有什麼操作你對xml檔案就應能實現什麼操作。一般而言,對資料庫的操作包括以下幾種:新建資料庫、查詢資料庫、修改資料庫和刪除資料庫。那麼對應xml檔案就是新建xml檔案、查詢xml檔案的指定節點的值,修改xml檔案中節點的值和刪除xml檔案中節點的值。

首先我們認識一下xml檔案有哪幾種形式。下面我列出一些常用的xml檔案的形式:

example1.xml:
<?xml version="1.0" ?>
<Hello>World</Hello>

example2.xml:
<?xml version="1.0" ?>
<poetry>
       <verse>
               Alas
                 Great World
                       Alas (again)
       </verse>
</poetry>

example3.xml:
<?xml version="1.0" ?>
<shapes>
       <circle name="int-based" x="20" y="30" r="50" />
       <point name="float-based" x="3.5" y="52.1" />
</shapes>

example4.xml:
<?xml version="1.0" ?>
<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>

上面的例子摘自《TinyXML Tutorial 中文指南》。上面有四個例子,你看到了xml檔案的幾種表現形式?我看到了本質來說不過是兩種表現形式:屬性值值在尖括號內,如<Window name="MainFrame" x="5" y="15" w="400" h="250" />和文字在尖括號外,如<Welcome>Welcome to MyApp</Welcome>。

鑑於example4.xml比較複雜,下面我將以此為例介紹tinyxml的使用。

Tinyxml使用了兩種編譯選擇:使用標準C的char *型別或者使用STL中的std::string,其中使用前處理器TIXML_USE_STL進行控制,即新增了TIXML_USE_STL為使用std::string的。鑑於STL的廣泛使用以及其強大功能,下面我以使用std::string的tinyxml說明。

首先使用VS 2010開啟tinyxmlSTL.dsp的工程檔案,將其編譯成一個靜態庫,debug版本為:tinyxmld_STL.lib,然後開始測試tinyxml庫。我的測試計劃是這樣的:首先使用tinyxml庫建立example4.xml,然後將其讀出來,然後查詢指定節點的屬性或文字,再修改example4.xml(修改其中的一些節點值和刪除其中一個節點,增加一個節點),然後再讀出來以判斷是否修改成功。具體是在VS 2010上新建一個控制檯工程:Test,注意使用多位元組字符集進行編譯,同時新增。首先是建立xml檔案的程式碼:

/*!
*  /brief 建立xml檔案。
*
*  /param XmlFile xml檔案全路徑。
*  /return 是否成功。true為成功,false表示失敗。
*/
bool CreateXml(std::string XmlFile)
{
// 定義一個TiXmlDocument類指標
TiXmlDocument *pDoc = new TiXmlDocument;
if (NULL==pDoc)
{
return false;
}
TiXmlDeclaration *pDeclaration = new TiXmlDeclaration(_T("1.0"),_T(""),_T(""));
if (NULL==pDeclaration)
{
return false;
}
pDoc->LinkEndChild(pDeclaration);
// 生成一個根節點:MyApp
TiXmlElement *pRootEle = new TiXmlElement(_T("MyApp"));
if (NULL==pRootEle)
{
return false;
}
pDoc->LinkEndChild(pRootEle);
// 生成子節點:Messages
TiXmlElement *pMsg = new TiXmlElement(_T("Messages"));
if (NULL==pMsg)
{
return false;
}
pRootEle->LinkEndChild(pMsg);
// 生成子節點:Welcome
TiXmlElement *pWelcome = new TiXmlElement(_T("Welcome"));
if (NULL==pWelcome)
{
return false;
}
pMsg->LinkEndChild(pWelcome);
// 設定Welcome節點的值
std::string strValue = _T("Welcome to MyApp");
TiXmlText *pWelcomeValue = new TiXmlText(strValue);
pWelcome->LinkEndChild(pWelcomeValue);
// 生成子節點:Farewell
TiXmlElement *pFarewell = new TiXmlElement(_T("Farewell"));
if (NULL==pFarewell)
{
return false;
}
pMsg->LinkEndChild(pFarewell);
// 設定Farewell節點的值
strValue = _T("Thank you for using MyApp");
TiXmlText *pFarewellValue = new TiXmlText(strValue);
pFarewell->LinkEndChild(pFarewellValue);
// 生成子節點:Windows
TiXmlElement *pWindows = new TiXmlElement(_T("Windows"));
if (NULL==pWindows)
{
return false;
}
pRootEle->LinkEndChild(pWindows);
// 生成子節點:Window
TiXmlElement *pWindow = new TiXmlElement(_T("Window"));
if (NULL==pWindow)
{
return false;
}
pWindows->LinkEndChild(pWindow);
    // 設定節點Window的值
    pWindow->SetAttribute(_T("name"),_T("MainFrame"));
    pWindow->SetAttribute(_T("x"),_T("5"));
pWindow->SetAttribute(_T("y"),_T("15"));
    pWindow->SetAttribute(_T("w"),_T("400"));
    pWindow->SetAttribute(_T("h"),_T("250"));
// 生成子節點:Window
TiXmlElement *pConnection  = new TiXmlElement(_T("Connection"));
if (NULL==pConnection)
{
return false;
}
pRootEle->LinkEndChild(pConnection);
// 設定節點Connection的值
pConnection->SetAttribute(_T("ip"),_T("192.168.0.1"));
pConnection->SetAttribute(_T("timeout"),_T("123.456000"));
    pDoc->SaveFile(XmlFile);
return true;
}

不知你注意到上面的規律沒有?首先父節點連線位元組點使用函式LinkEndChild,使用方法是:pParentNode-> LinkEndChild(pChild);其次設定類似這種結構<Window name="MainFrame" x="5" y="15" w="400" h="250" />採用SetAttribute函式,這個函式有兩個引數,前一個參數列示鍵,後一個參數列示鍵值,設定<Farewell>Thank you for using MyApp</Farewell>這種結構採用TiXmlText類,使用LinkEndChild函式進行連結。

上面是建立xml檔案的程式碼,下面介紹讀取xml檔案的程式碼。列印整個xml檔案的程式碼很簡單,程式碼如下:

/*!
*  /brief 列印xml檔案。
*
*  /param XmlFile xml檔案全路徑。
*  /return 是否成功。true為成功,false表示失敗。
*/
bool PaintXml(std::string XmlFile)
{
// 定義一個TiXmlDocument類指標
TiXmlDocument *pDoc = new TiXmlDocument();
if (NULL==pDoc)
{
return false;
}
pDoc->LoadFile(XmlFile);
    pDoc->Print();
return true;
}

下次介紹使用tinyxml庫對xml檔案進行查詢指定節點、刪除指定節點、修改指定節點和增加節點的用法。


 Visual C++ TinyXml快速入門(二)

在 Visual C++ TinyXml快速入門(一)中,我介紹了使用TinyXml庫如何建立和列印xml檔案,下面我介紹使用tinyxml庫對xml檔案進行一系列的操作,包括獲取xml檔案宣告,查詢指定節點、刪除指定節點、修改指定節點和增加節點的用法。在《TinyXml快速入門(一)》中我們知道xml檔案中的一個節點元素實際包含兩種值:屬性和文字。其中屬性在我看來可以看作是STL中的map,一個屬性帶一個屬性值,map中也是一個鍵帶一個鍵值。因此查詢指定節點、刪除指定節點和增加節點必然是需要實現兩種方法,刪除指定節點只需要實現一種方法。鑑於內容較多,在本文中介紹獲取xml檔案宣告,查詢指定節點、刪除指定節點的做法,修改指定節點和增加節點的做法在後續的文章介紹。

首先是獲取xml檔案宣告。xml檔案宣告包括三方面的內容:Version、Standalone和Encoding。其原始碼如下:

/*!
*  /brief 獲取xml檔案的宣告。
*
*  /param XmlFile xml檔案全路徑。
*  /param strVersion  Version屬性值
*  /param strStandalone Standalone屬性值
*  /param strEncoding Encoding屬性值
*  /return 是否成功。true為成功,false表示失敗。
*/
bool GetXmlDeclare(std::string XmlFile,
  std::string &strVersion,
  std::string &strStandalone,
  std::string &strEncoding)
{
// 定義一個TiXmlDocument類指標
TiXmlDocument *pDoc = new TiXmlDocument();
if (NULL==pDoc)
{
return false;
}
pDoc->LoadFile(XmlFile);
 TiXmlNode* pXmlFirst = pDoc->FirstChild();   
 if (NULL != pXmlFirst)  
     {  
          TiXmlDeclaration* pXmlDec = pXmlFirst->ToDeclaration();  
          if (NULL != pXmlDec)  
          {  
              strVersion = pXmlDec->Version();
              strStandalone = pXmlDec->Standalone();
              strEncoding = pXmlDec->Encoding();
     }
 }
 return true;
}
我們發現無論查詢節點、刪除節點、修改節點和增加節點,其實都離不開一個函式,就是根據節點名獲取相關節點指標。那麼我們就先實現一個根據節點名獲取節點指標的函式:

/*!
*  /brief 通過根節點和節點名獲取節點指標。
*
*  /param pRootEle   xml檔案的根節點。
*  /param strNodeName  要查詢的節點名
*  /param Node      需要查詢的節點指標
*  /return 是否找到。true為找到相應節點指標,false表示沒有找到相應節點指標。
*/
bool GetNodePointerByName(TiXmlElement* pRootEle,std::string &strNodeName,TiXmlElement* &Node)
{
// 假如等於根節點名,就退出
     if (strNodeName==pRootEle->Value())
     {
         Node = pRootEle;
return true;
     }
 TiXmlElement* pEle = pRootEle;  
      for (pEle = pRootEle->FirstChildElement(); pEle; pEle = pEle->NextSiblingElement())  
    {  
          //遞迴處理子節點,獲取節點指標
          if(GetNodePointerByName(pEle,strNodeName,Node))
 return true;
     }  
return false;
} 
有了這個函式,我們就很容易實現查詢節點的相應文字或屬性值。

 /*!
*  /brief 通過節點查詢。
*
*  /param XmlFile   xml檔案全路徑。
*  /param strNodeName  要查詢的節點名
*  /param strText      要查詢的節點文字
*  /return 是否成功。true為成功,false表示失敗。
*/
bool QueryNode_Text(std::string XmlFile,std::string strNodeName,std::string &strText)
{
// 定義一個TiXmlDocument類指標
TiXmlDocument *pDoc = new TiXmlDocument();
if (NULL==pDoc)
{
return false;
}
pDoc->LoadFile(XmlFile);
TiXmlElement *pRootEle = pDoc->RootElement();
if (NULL==pRootEle)
{
return false;
}
   TiXmlElement *pNode = NULL;
   GetNodePointerByName(pRootEle,strNodeName,pNode);
   if (NULL!=pNode)
   {
        strText = pNode->GetText(); 
return true;
   }
   else
   {
   return false;
   }

}
/*!
*  /brief 通過節點查詢。
*
*  /param XmlFile   xml檔案全路徑。
*  /param strNodeName  要查詢的節點名
*  /param AttMap      要查詢的屬性值,這是一個map,前一個為屬性名,後一個為屬性值
*  /return 是否成功。true為成功,false表示失敗。
*/
bool QueryNode_Attribute(std::string XmlFile,std::string strNodeName,std::map<std::string,std::string> &AttMap)
{
// 定義一個TiXmlDocument類指標
    typedef std::pair <std::string,std::string> String_Pair;
TiXmlDocument *pDoc = new TiXmlDocument();
if (NULL==pDoc)
{
return false;
}
pDoc->LoadFile(XmlFile);
TiXmlElement *pRootEle = pDoc->RootElement();
if (NULL==pRootEle)
{
return false;
}
TiXmlElement *pNode = NULL;
GetNodePointerByName(pRootEle,strNodeName,pNode);
if (NULL!=pNode)
{
TiXmlAttribute* pAttr = NULL; 
for (pAttr = pNode->FirstAttribute(); pAttr; pAttr = pAttr->Next())  
{  
std::string strAttName = pAttr->Name();
std::string strAttValue = pAttr->Value();
AttMap.insert(String_Pair(strAttName,strAttValue));
}  
return true;
}
else
{
return false;
}
return true;
}
下面是刪除指定節點的函式,其中考慮了刪除根節點的情況:

/*!
*  /brief 刪除指定節點的值。
*
*  /param XmlFile xml檔案全路徑。
*  /param strNodeName 指定的節點名。
*  /return 是否成功。true為成功,false表示失敗。
*/
bool DelNode(std::string XmlFile,std::string strNodeName)
{
// 定義一個TiXmlDocument類指標
TiXmlDocument *pDoc = new TiXmlDocument();
if (NULL==pDoc)
{
return false;
}
pDoc->LoadFile(XmlFile);
TiXmlElement *pRootEle = pDoc->RootElement();
if (NULL==pRootEle)
{
return false;
}
TiXmlElement *pNode = NULL;
GetNodePointerByName(pRootEle,strNodeName,pNode);
// 假如是根節點
if (pRootEle==pNode)
{
          if(pDoc->RemoveChild(pRootEle))
 {
               pDoc->SaveFile(XmlFile);
  return true;
 }
 else 
 return false;
}
// 假如是其它節點
if (NULL!=pNode)
{
TiXmlNode *pParNode =  pNode->Parent();
if (NULL==pParNode)
{
               return false;
}

TiXmlElement* pParentEle = pParNode->ToElement();
if (NULL!=pParentEle)
{
            if(pParentEle->RemoveChild(pNode))
                 pDoc->SaveFile(XmlFile);
else
return false;
}
}
else
{
          return false;
}
return false;
}

Visual C++ TinyXml快速入門(三)

在Visual C++ TinyXml快速入門(二)中,介紹使用tinyxml庫獲取xml檔案宣告,查詢指定節點、刪除指定節點的做法。在本文中繼續介紹修改指定節點和增加節點的做法。修改節點其實和查詢指定節點的值有點類似,也分為兩個函式,一個實現修改文字。另一個負責修改屬性。

/*!
*  /brief 修改指定節點的文字。
*
*  /param XmlFile xml檔案全路徑。
*  /param strNodeName 指定的節點名。
*  /param strText 重新設定的文字的值
*  /return 是否成功。true為成功,false表示失敗。
*/
bool ModifyNode_Text(std::string XmlFile,std::string strNodeName,std::string strText)
{
// 定義一個TiXmlDocument類指標
TiXmlDocument *pDoc = new TiXmlDocument();
if (NULL==pDoc)
{
return false;
}
pDoc->LoadFile(XmlFile);
TiXmlElement *pRootEle = pDoc->RootElement();
if (NULL==pRootEle)
{
return false;
}
TiXmlElement *pNode = NULL;
GetNodePointerByName(pRootEle,strNodeName,pNode);
if (NULL!=pNode)
{
        pNode->Clear();  // 首先清除所有文字
// 然後插入文字,儲存檔案
TiXmlText *pValue = new TiXmlText(strText);
pNode->LinkEndChild(pValue);
pDoc->SaveFile(XmlFile);
return true;
}
else
return false;
}
/*!
*  /brief 修改指定節點的屬性值。
*
*  /param XmlFile xml檔案全路徑。
*  /param strNodeName 指定的節點名。
*  /param AttMap 重新設定的屬性值,這是一個map,前一個為屬性名,後一個為屬性值
*  /return 是否成功。true為成功,false表示失敗。
*/
bool ModifyNode_Attribute(std::string XmlFile,std::string strNodeName,
std::map<std::string,std::string> &AttMap)
{
typedef std::pair <std::string,std::string> String_Pair;
// 定義一個TiXmlDocument類指標
TiXmlDocument *pDoc = new TiXmlDocument();
if (NULL==pDoc)
{
return false;
}
pDoc->LoadFile(XmlFile);
TiXmlElement *pRootEle = pDoc->RootElement();
if (NULL==pRootEle)
{
return false;
}
 
TiXmlElement *pNode = NULL;
GetNodePointerByName(pRootEle,strNodeName,pNode);
if (NULL!=pNode)
{
TiXmlAttribute* pAttr = NULL; 
        std::string strAttName = _T("");
        std::string strAttValue = _T("");
for (pAttr = pNode->FirstAttribute(); pAttr; pAttr = pAttr->Next())  
{  
strAttName = pAttr->Name();
std::map<std::string,std::string>::iterator iter;
for (iter=AttMap.begin();iter!=AttMap.end();iter++)
{
if (strAttName==iter->first)
{
                    pAttr->SetValue(iter->second);
}
}
}  
pDoc->SaveFile(XmlFile);
return true;
}
else
{
return false;
}
}

對於ModifyNode_Attribute函式,這裡稍微介紹一下如何使用,比如對於下面這樣一個xml檔案:

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<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>
我們如果要修改節點的Connection的ip為192.168.0.100,timeout為1000,我們可以這樣用:

std::string XmlFile = _T("E://TestTinyxml//example4.xml");
std::string strNodeName = _T("Connection");
   typedef std::pair <std::string,std::string> String_Pair;
   std::map<std::string,std::string> AttMap;
   AttMap.insert(String_Pair(_T("ip"),_T("192.168.0.100")));
   AttMap.insert(String_Pair(_T("timeout"),_T("1000")));
   ModifyNode_Attribute(XmlFile,strNodeName,AttMap);
下面是增加節點的兩個函式:

/*!
*  /brief 增加指定節點的文字。
*
*  /param XmlFile xml檔案全路徑。
*  /param strParNodeName 要增加的節點的父節點。
*  /param strNodeName 指定的節點名。
*  /param strText 要增加的文字
*  /return 是否成功。true為成功,false表示失敗。
*/
bool AddNode_Text(std::string XmlFile,std::string strParNodeName,std::string strNodeName,std::string strText)
{
// 定義一個TiXmlDocument類指標
TiXmlDocument *pDoc = new TiXmlDocument();
if (NULL==pDoc)
{
return false;
}
pDoc->LoadFile(XmlFile);
TiXmlElement *pRootEle = pDoc->RootElement();
if (NULL==pRootEle)
{
return false;
}
TiXmlElement *pNode = NULL;
GetNodePointerByName(pRootEle,strParNodeName,pNode);
if (NULL!=pNode)
{
// 生成子節點:pNewNode
TiXmlElement *pNewNode = new TiXmlElement(strNodeName);
if (NULL==pNewNode)
{
return false;
}
// 設定節點文字,然後插入節點
TiXmlText *pNewValue = new TiXmlText(strText);
pNewNode->LinkEndChild(pNewValue);
        pNode->InsertEndChild(*pNewNode);
        pDoc->SaveFile(XmlFile);
        return true;
}
else
    return false;
    
}
/*!
*  /brief 增加節點。
*
*  /param XmlFile xml檔案全路徑。
*  /param strParNodeName 要增加的節點的父節點。
*  /param strNodeName 指定的節點名。
*  /param AttMap 要增加的節點設定的屬性值,這是一個map,前一個為屬性名,後一個為屬性值
*  /return 是否成功。true為成功,false表示失敗。
*/
bool AddNode_Attribute(std::string XmlFile,std::string strParNodeName,std::string strNodeName,std::map<std::string,std::string> &AttMap)
{
// 定義一個TiXmlDocument類指標
TiXmlDocument *pDoc = new TiXmlDocument();
if (NULL==pDoc)
{
return false;
}
pDoc->LoadFile(XmlFile);
TiXmlElement *pRootEle = pDoc->RootElement();
if (NULL==pRootEle)
{
return false;
}
TiXmlElement *pNode = NULL;
GetNodePointerByName(pRootEle,strParNodeName,pNode);
if (NULL!=pNode)
{
// 生成子節點:pNewNode
TiXmlElement *pNewNode = new TiXmlElement(strNodeName);
if (NULL==pNewNode)
{
return false;
}
// 設定節點的屬性值,然後插入節點
std::map<std::string,std::string>::iterator iter;
for (iter=AttMap.begin();iter!=AttMap.end();iter++)
{
pNewNode->SetAttribute(iter->first,iter->second);
}
pNode->InsertEndChild(*pNewNode);
pDoc->SaveFile(XmlFile);
return true;
}
else
return false;
}


相關文章