读取和设置xml配置文件是最常用的操作,试用了几个C++的XML解析器,个人感觉TinyXML是使用起来最舒服的,因为它的API接口和Java的十分类似,面向对象性很好。
TinyXML是一个开源的解析XML的解析库,能够用于C++,能够在Windows或Linux中编译。这个解析库的模型通过解析XML文件,然后在内存中生成DOM模型,从而让我们很方便的遍历这棵XML树。
DOM模型即文档对象模型,是将整个文档分成多个元素(如书、章、节、段等),并利用树型结构表示这些元素之间的顺序关系以及嵌套包含关系。
如下是一个XML片段:
file:///C:/Users/MHAPDR~1/AppData/Local/Temp/enhtmlclip/Image.gif   <Persons>
        <Person ID="1">
            <name>周星星</name>
            <age>20</age>
        </Person>
        <Person ID="2">
            <name>白晶晶</name>
            <age>18</age>
        </Person>
    </Persons>
 在TinyXML中,根据XML的各种元素来定义了一些类:
TiXmlBase:整个TinyXML模型的基类。
TiXmlAttribute:对应于XML中的元素的属性。
TiXmlNode:对应于DOM结构中的节点。
TiXmlComment:对应于XML中的注释
TiXmlDeclaration:对应于XML中的申明部分,即<?versiong="1.0" ?>。
TiXmlDocument:对应于XML的整个文档。
TiXmlElement:对应于XML的元素。
TiXmlText:对应于XML的文字部分
TiXmlUnknown:对应于XML的未知部分。 
TiXmlHandler:定义了针对XML的一些操作。
TinyXML是个解析库,主要由DOM模型类(TiXmlBase、TiXmlNode、TiXmlAttribute、TiXmlComment、TiXmlDeclaration、TiXmlElement、TiXmlText、TiXmlUnknown)和操作类(TiXmlHandler)构成。它由两个头文件(.h文件)和四个CPP文件(.cpp文件)构成,用的时候,只要将(tinyxml.h、tinystr.h、tinystr.cpp、tinyxml.cpp、tinyxmlerror.cpp、tinyxmlparser.cpp)导入工程就可以用它的东西了。如果需要,可以将它做成自己的DLL来调用。举个例子就可以说明一切。。。
对应的XML文件:
<Persons>
    <Person ID="1">
        <name>phinecos</name>
        <age>22</age>
    </Person>
</Persons>
读写XML文件的程序代码:
#include <iostream>
#include "tinyxml.h"
#include "tinystr.h"
#include <string>
#include <windows.h>
#include <atlstr.h>
using namespace std;
CString GetAppPath()
file:///C:/Users/MHAPDR~1/AppData/Local/Temp/enhtmlclip/Image(1).gif{//获取应用程序根目录
file:///C:/Users/MHAPDR~1/AppData/Local/Temp/enhtmlclip/Image(2).gif    TCHAR modulePath[MAX_PATH];
    GetModuleFileName(NULL, modulePath, MAX_PATH);
    CString strModulePath(modulePath);
    strModulePath = strModulePath.Left(strModulePath.ReverseFind(_T('\\')));
    return strModulePath;
file:///C:/Users/MHAPDR~1/AppData/Local/Temp/enhtmlclip/Image(3).gif}
bool CreateXmlFile(string& szFileName)
{//创建xml文件,szFilePath为文件保存的路径,若创建成功返回true,否则false
    try
file:///C:/Users/MHAPDR~1/AppData/Local/Temp/enhtmlclip/Image(4).gif    {
        //创建一个XML的文档对象。
        TiXmlDocument *myDocument = new TiXmlDocument();
        //创建一个根元素并连接。
        TiXmlElement *RootElement = new TiXmlElement("Persons");
        myDocument->LinkEndChild(RootElement);
        //创建一个Person元素并连接。
        TiXmlElement *PersonElement = new TiXmlElement("Person");
        RootElement->LinkEndChild(PersonElement);
        //设置Person元素的属性。
        PersonElement->SetAttribute("ID", "1");
        //创建name元素、age元素并连接。
        TiXmlElement *NameElement = new TiXmlElement("name");
        TiXmlElement *AgeElement = new TiXmlElement("age");
        PersonElement->LinkEndChild(NameElement);
        PersonElement->LinkEndChild(AgeElement);
        //设置name元素和age元素的内容并连接。
        TiXmlText *NameContent = new TiXmlText("周星星");
        TiXmlText *AgeContent = new TiXmlText("22");
        NameElement->LinkEndChild(NameContent);
        AgeElement->LinkEndChild(AgeContent);
        CString appPath = GetAppPath();
        string seperator = "\\";
        string fullPath = appPath.GetBuffer(0) +seperator+szFileName;
        myDocument->SaveFile(fullPath.c_str());//保存到文件
file:///C:/Users/MHAPDR~1/AppData/Local/Temp/enhtmlclip/Image(5).gif    }
    catch (string& e)
    {
        return false;
    }
    return true;
}
bool ReadXmlFile(string& szFileName)
{//读取Xml文件,并遍历
    try
    {
        CString appPath = GetAppPath();
        string seperator = "\\";
        string fullPath = appPath.GetBuffer(0) +seperator+szFileName;
        //创建一个XML的文档对象。
        TiXmlDocument *myDocument = new TiXmlDocument(fullPath.c_str());
        myDocument->LoadFile();
        //获得根元素,即Persons。
        TiXmlElement *RootElement = myDocument->RootElement();
        //输出根元素名称,即输出Persons。
        cout << RootElement->Value() << endl;
        //获得第一个Person节点。
        TiXmlElement *FirstPerson = RootElement->FirstChildElement();
        //获得第一个Person的name节点和age节点和ID属性。
        TiXmlElement *NameElement = FirstPerson->FirstChildElement();
        TiXmlElement *AgeElement = NameElement->NextSiblingElement();
        TiXmlAttribute *IDAttribute = FirstPerson->FirstAttribute();
        //输出第一个Person的name内容,即周星星;age内容,即;ID属性,即。
        cout << NameElement->FirstChild()->Value() << endl;
        cout << AgeElement->FirstChild()->Value() << endl;
        cout << IDAttribute->Value()<< endl;
    }
    catch (string& e)
    {
        return false;
    }
    return true;
}
int main()
{
    string fileName = "info.xml";
    CreateXmlFile(fileName);
    ReadXmlFile(fileName);
}
                        
写本文的目的是为了方便大家了解C++ MSXML操作方法。
当然,C++中对MSXML的调用有多种,本文采用的方法是完全参照MSXML SDK提供的文档进行操作。
如果有什么错误,欢迎指正。
代码框架是基于vs2008 MFC 对话框程序(UNICODE)。对话框程序需要读者自己创建。
[cpp] view plain copy
- #include <msxml6.h>  
 - #include <comutil.h>  
 - #pragma comment(lib, "comsuppwd.lib")  
 - void CXmlSampleDlg::OnBnClickedButton1()//按钮事件  
 - {  
 -     CoInitialize(NULL);  
 -     CComPtr<IXMLDOMDocument> spXmldoc;  
 -     HRESULT hr = spXmldoc.CoCreateInstance(L"MSXML2.DOMDocument.6.0");  
 -   
 -     if(SUCCEEDED(hr))  
 -     {  
 -         VARIANT_BOOL isSuccessFul;  
 -         CComVariant varXmlFile(L"a.xml");  
 -   
 -         spXmldoc->put_async(VARIANT_FALSE);  
 -         HRESULT hr= spXmldoc->load(varXmlFile, &isSuccessFul);  
 -   
 -         if(isSuccessFul==VARIANT_TRUE)  
 -         {  
 -             CComBSTR bstrXml;  
 -             CComPtr<IXMLDOMElement> spRoot=NULL;  
 -             CComPtr<IXMLDOMElement> spTheBook=NULL;  
 -             CComPtr<IXMLDOMElement> spTheElem=NULL;  
 -             CComPtr<IXMLDOMNode> spNewNode=NULL;  
 -   
 -             hr = spXmldoc->get_documentElement(&spRoot);  
 -             spRoot->get_xml(&bstrXml);  
 -             AfxMessageBox(L"1, 原始的XML");  
 -             AfxMessageBox(bstrXml);  
 -   
 -   
 -             spXmldoc->createElement(L"book", &spTheBook);  
 -             spXmldoc->createElement(L"name", &spTheElem);  
 -             spTheElem->put_text(L"新书");  
 -             spTheBook->appendChild(spTheElem, &spNewNode);  
 -             spTheElem.Release();  
 -             spNewNode.Release();  
 -   
 -             spXmldoc->createElement(L"price", &spTheElem);  
 -             spTheElem->put_text(L"20");  
 -             spTheBook->appendChild(spTheElem, &spNewNode);  
 -             spTheElem.Release();  
 -             spNewNode.Release();  
 -   
 -             spXmldoc->createElement(L"memo", &spTheElem);  
 -             spTheElem->put_text(L"新书的更好看。");  
 -             spTheBook->appendChild(spTheElem, &spNewNode);  
 -             spNewNode.Release();  
 -             spTheElem.Release();  
 -   
 -             spRoot->appendChild(spTheBook, &spNewNode);  
 -             spNewNode.Release();  
 -             spTheBook.Release();  
 -   
 -             spRoot->get_xml(&bstrXml);  
 -             AfxMessageBox(L"2, 新建一本书完成");  
 -             AfxMessageBox(bstrXml);  
 -             ////---  新建一本书完成 ----  
 -   
 -   
 -             ////---  下面对《哈里波特》做一些修改。 ----  
 -             ////---  查询找《哈里波特》----  
 -             CComPtr<IXMLDOMNode> spTheNode=NULL;  
 -             spRoot->selectSingleNode(L"/books/book[name='哈里波特']", &spTheNode);  
 -             hr=spTheNode.QueryInterface(&spTheBook);  
 -             spTheNode.Release();  
 -   
 -             spTheBook->get_xml(&bstrXml);  
 -             AfxMessageBox(L"3,《哈里波特》的XML");  
 -             AfxMessageBox(bstrXml);  
 -   
 -             ////---  此时修改这本书的价格 -----  
 -             CComPtr<IXMLDOMNodeList> spNodeList=NULL;  
 -             CComPtr<IXMLDOMNode> spListItem=NULL;  
 -             spTheBook->get_childNodes(&spNodeList);  
 -             spNodeList->get_item(1, &spListItem);  
 -             spNodeList.Release();  
 -             spListItem->put_text(L"15");  
 -   
 -             ////---  另外还想加一个属性id,值为B01 ----  
 -             CComVariant varId(L"B01");  
 -             spTheBook->setAttribute(L"id", varId);  
 -             varId.Clear();  
 -   
 -             spTheBook->get_xml(&bstrXml);  
 -             spTheBook.Release();  
 -             AfxMessageBox(L"4, 对《哈里波特》修改完成。");  
 -             AfxMessageBox(bstrXml);  
 -             ////---  对《哈里波特》修改完成。 ----  
 -   
 -   
 -             ////---  要用id属性删除《三国演义》这本书  ----  
 -             spRoot->selectSingleNode(L"/books/book[@id='B02']", &spTheNode);  
 -             hr=spTheNode.QueryInterface(&spTheBook);  
 -             spTheNode.Release();  
 -   
 -             spTheBook->get_xml(&bstrXml);  
 -             AfxMessageBox(L"5, 《三国演义》的XML");  
 -             AfxMessageBox(bstrXml);  
 -   
 -             CComPtr<IXMLDOMNode> spParentNode=NULL;  
 -             spTheBook->get_parentNode(&spParentNode);  
 -             spParentNode->removeChild(spTheBook, &spTheNode);  
 -             spTheNode.Release();  
 -             spParentNode.Release();  
 -             spTheBook.Release();  
 -   
 -             spRoot->get_xml(&bstrXml);  
 -             AfxMessageBox(L"6, 删除《三国演义》后的XML");  
 -             AfxMessageBox(bstrXml);  
 -   
 -   
 -             ////---  再将所有价格低于10的书删除  ----  
 -             spRoot->selectNodes(L"/books/book[price<10]", &spNodeList);  
 -             CComQIPtr<IXMLDOMSelection> spSomeBooks=spNodeList;  
 -             spNodeList.Release();  
 -   
 -             spSomeBooks->removeAll();  
 -             spSomeBooks.Release();  
 -   
 -             spXmldoc->get_xml(&bstrXml);  
 -             AfxMessageBox(L"7, 已经删除价格低于10的书");  
 -             AfxMessageBox(bstrXml);  
 -   
 -             spRoot.Release();  
 -             bstrXml.Empty();  
 -   
 -             //spXmldoc->save(varXmlFile); //保存xml。  
 -         }  
 -         varXmlFile.ClearToZero();  
 -     }  
 -   
 -     spXmldoc.Release();  
 -   
 -     CoUninitialize();  
 - }  
 
a.xml
[xhtml] view plain copy
- <?xml version="1.0" encoding="UTF-8"?>  
 - <books>  
 -  <book>  
 -   <name>哈里波特</name>  
 -   <price>10</price>  
 -   <memo>这是一本很好看的书。</memo>  
 -  </book>  
 -  <book id="B02">  
 -   <name>三国演义</name>  
 -   <price>10</price>  
 -   <memo>四大名著之一。</memo>  
 -  </book>  
 -  <book id="B03">  
 -   <name>水浒</name>  
 -   <price>6</price>  
 -   <memo>四大名著之一。</memo>  
 -  </book>  
 -  <book id="B04">  
 -   <name>红楼</name>  
 -   <price>5</price>  
 -   <memo>四大名著之一。</memo>  
 -  </book>  
 - </books>   
 
 最近使用TinyXML进行C++ XML解析,感觉使用起来比较简单,很容易上手,本文给出一个使用TinyXML进行XML解析的简单例子,很多复杂的应用都可以基于本例子的方法来完成。以后的文章里会讲解使用Xerces进行C++ XML解析的例子,希望大家一起交流。
  TinyXML是一个开源的解析XML的解析库,能够用于C++,能够在Windows或Linux中编译。这个解析库的模型通过解析XML文件,然后在内存中生成DOM模型,从而让我们很方便的遍历这棵XML树。
  DOM模型即文档对象模型,是将整个文档分成多个元素(如书、章、节、段等),并利用树型结构表示这些元素之间的顺序关系以及嵌套包含关系。
  首先从网上下载TinyXML的库,文件夹的名字是TinyXpath,在工程里做如下配置:
  在附加包含路径里添加:你的tinyxpath路径/tinyxpath/include
  在附加库路径里添加:你的tinyxpath路径/tinyxpath/lib
  在对象/库路径里添加:tinyxpathd.lib,如果使用release版本,则是tinyxpath.lib。
  另外,由于我开发的项目是多线程的,所以设置了多线程的环境,因此使用TinyXML没有出现问题。本人将TinyXML写在一个单独的C++工程进行测试,发现如果不设置多线程的环境,会出现链接错误。我觉得原因可能是TinyXML使用了多线程环境,因此需要设置多线程的环境。在工程/设置下的C/C++选项卡中,选择Code Generation,在Use run-time library中选择Debug MultiThreaed DLL即可。
  本例的XML文件Students.xml如下:
  <Class name="计算机软件班">
  <Students>
  <student name="张三" studentNo="13031001" sex="男" age="22">
  <phone>88208888</phone>
  <address>西安市太白南路二号</address>
  </student>
  <student name="李四" studentNo="13031002" sex="男" age="20">
  <phone>88206666</phone>
  <address>西安市光华路</address>
  </student>
  </Students>
  </Class>
  程序代码XmlParseExample.cpp如下所示:
  #include <iostream>
  #include <string>
  #include <tinyxml.h>
  using std::string;
  int main()
  {
  TiXmlDocument* myDocument = new TiXmlDocument();
  myDocument->LoadFile("Students.xml");
  TiXmlElement* rootElement = myDocument->RootElement(); //Class
  TiXmlElement* studentsElement = rootElement->FirstChildElement(); //Students
  TiXmlElement* studentElement = studentsElement->FirstChildElement(); //Students
  while ( studentElement ) {
  TiXmlAttribute* attributeOfStudent = studentElement->FirstAttribute(); //获得student的name属性
  while ( attributeOfStudent ) {
  std::cout 《 attributeOfStudent->Name() 《 " : " 《 attributeOfStudent->Value() 《 std::endl;
  attributeOfStudent = attributeOfStudent->Next();
  }
  TiXmlElement* phoneElement = studentElement->FirstChildElement();//获得student的phone元素
  std::cout 《 "phone" 《 " : " 《 phoneElement->GetText() 《 std::endl;
  TiXmlElement* addressElement = phoneElement->NextSiblingElement();
  std::cout 《 "address" 《 " : " 《 phoneElement->GetText() 《 std::endl;
  studentElement = studentElement->NextSiblingElement();
  }
  return 0;
  }
  程序运行结果如下:
  name : 张三
  studentNo : 13031001
  sex : 男
  age : 22
  phone : 88208888
  address : 88208888
  name : 李四
  studentNo : 13031002
  sex : 男
  age : 20
  phone : 88206666
  address : 88206666
  本例中使用的是对xml文件进行解析,很容易掌握,但是很多开发人员不知道如何对xml 字符流(非xml文件)进行解析,我看了TinyXML提供的源代码,里面可以使用如下方法对xml流解析。对应于上例,代码如下:
  string xmlString =
  "<Class name=\"计算机软件班\">
  <Students>
  <student name=\"张三\" studentNo=\"13031001\" sex=\"男\" age=\"22\">
  <phone>88208888</phone>
  <address>西安市太白南路二号</address>
  </student>
  <student name=\"李四\" studentNo=\"13031002\" sex=\"男\" age=\"20\">
  <phone>88206666</phone>
  <address>西安市光华路</address>
  </student>
  </Students>
  </Class>";
  TiXmlDocument* myDocument = new TiXmlDocument();
  myDocument->Parse(xmlString.c_str());
  使用Parse函数就可以解析XML字符流了,这是很多开发者不太熟悉的情况。
  如果开发者开发特定应用,就可以使用上述类似方法,可能不需要完全处理每一个属性,比如可以对属性名进行判断,只处理自己需要的属性,或者自己需要的xml元素。还可以使用TinyXML的方法创建xml元素和xml属性,或者设置xml元素和属性对应的值,等等,如果读者想要类似的例子,可以留言写出。
  下面介绍TinyXML的一些类。在TinyXML中,根据XML的各种元素来定义了一些类:
  TiXmlBase:整个TinyXML模型的基类。
  TiXmlAttribute:对应于XML中的元素的属性。
  TiXmlNode:对应于DOM结构中的节点。
  TiXmlComment:对应于XML中的注释
  TiXmlDeclaration:对应于XML中的申明部分,<?versiong="1.0" ?>。
  TiXmlDocument:对应于XML的整个文档。
  TiXmlElement:对应于XML的元素。
  TiXmlText:对应于XML的文字部分
  TiXmlUnknown:对应于XML的未知部分。
  TiXmlHandler:定义了针对XML的一些操作。