MSXML應用總結

 MSXML的DOM模型是符合W3C DOM標準的,而DOM API在Windows中以COM接口的形式提供,關於COM請你們查閱相關資料。簡單來講,COM提供了一個環境和一套規則,使接口的設計實現到對象的建立、使用和釋放都標準化,從而使COM支持跨平臺和跨語言;更重要的是,遵照COM規範使咱們代碼的接口與實現分離,將程序框架的穩定與擴展統一塊兒來,對於使用COM接口的人則更加簡單直觀。COM中一個很重要的概念是refcount,即接口對象的訪問計數,經過AddRef和Release兩個接口函數來控制。要想用好refcount仍是件較困難的事情,所以我推薦你們使用智能指針。使用智能指針就像使用一個簡單指針同樣,咱們徹底不用去關心指針指向內存空間的釋放。html

    本篇總結采用API版本是MSXML2.0。node

   

    首先咱們看一下經常使用的接口:c++

    IXMLDOMDocument:XML文檔接口,DOM樹結構的根結點,是對文檔訪問和操做的入口;編程

    IXMLDOMNode:節點接口,該接口是廣泛意義上的節點接口,不少類型節點接口都從它派生,包括IXMLDOMDocument;app

    IXMLDOMNodeList:節點列表接口,表示一組關聯的節點集合;該列表中的node元素經過index(從0開始)訪問,另外該接口中的元素仍是動態的,會隨着XML文檔的改變而更新;框架

    IXMLDOMNamedNodeMap:節點集合接口,也表示一組關聯節點的集合;不過與list不一樣的是,該集合是無序的,該接口經常使用於表示節點的屬性集,而且該接口也是動態的;異步

    IXMLDOMElement:元素接口,通常用來表示一個節點及其屬性;async

    IXMLDOMAttribute:節點屬性接口,對節點屬性進行訪問和操做;函數

    IXMLDOMText:節點中文本控制接口;測試

    IXMLDOMComment:XML文檔中的註釋接口;

    IXMLDOMParseError:出錯處理接口,包括了錯誤的詳細信息。

    以上都是最經常使用的DOM接口,還有一些接口沒有在此列出。對於接口來講,都有相應的智能指針接口,通常爲接口名加上Ptr,好比IXMLDOMDocument的智能指針接口爲IXMLDOMDocumentPtr。這裏有一個接口繼承關係示意圖:

MSXML應用總結 <wbr>開發篇(上)

    在VS2005環境下進行DOM應用開發,首先要設置DOM接口應用環境,在stdafx.h文件中加入語句: 

 

    #import <msxml3.dll> raw_interfaces_only

 

    若是你的系統文件夾下有msxml6.dll文件,#import語句將成生MSXML庫類型信息,通常會在你的工程編譯文件夾下生成msxml6.tlh和msxml6.tli兩個文件,打開看一下可知這兩個文件包含了一些COM接口類型及函數的聲明以及一些庫信息。實際上,#import指令使dll庫中的類型信息導出爲描述的COM接口的c++類頭文件。而「raw_interfaces_only」屬性使得生成文件只有msxml6.tlh一個,並且接口函數只有HRESULT返回類型一種形式,且省去了raw_前綴;若是去掉該屬性,則除了在msxml6.tlh文件中聲明帶raw_前綴的返回HRESULT類型的接口函數外,還會在msxml6.tlh中生成不帶raw_前綴的wrapper接口函數,並在msxml6.tli文件中生成返回接口指針類型的wrapper接口函數。所以咱們在應用DOM接口的時候,發現有兩套完成相同功能的接口函數,分別返回HRESULT類型和接口指針類型,就是由於上述緣由,這應該是Windows環境下COM接口描述的規則,比較深刻的介紹請參考這篇文章:http://www.cnblogs.com/xiaotaoliang/archive/2005/07/20/196257.html。爲了應用方便,咱們下面的示例代碼不必定用的都是加了「raw_interfaces_only」屬性後的接口函數,建議你們能夠去掉該屬性,此處只是加以說明。

    另一種加載DOM接口的方法是直接在工程環境中添加msxml庫的路徑,並連接msxml6.lib文件,這裏再也不詳述。

    設置DOM環境後,還要初始化COM應用環境,在應用線程初始化函數中調用CoInitialize,並在線程退出時調用CoUninitialize。

 

    如今咱們能夠用DOM接口來對xml文件進行操做了,我將按操做分類進行總結。

   

1、xml文件的加載和保存

    因爲DOM模型面向的是整個xml文件,所以咱們須要本身建立的接口只有IXMLDOMDocument一個,其餘接口都是從它直接或間接獲得的,xml文件的加載和保存函數也在IXMLDOMDocument接口中實現。建立IXMLDOMDocument接口的代碼以下:

 

    MSXML2::IXMLDOMDocumentPtr pXmlDoc;

    HRESULT hr = pXmlDoc.CreateInstance( __uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER);

    if( FAILED(hr))

        printf("Failed to create DOM document interface pointer.\n");

 

    加載xml文件代碼爲: 

 

    try

    {

        pXmlDoc->async = VARIANT_FALSE;

        pXmlDoc->validateOnParse = VARIANT_FALSE;

        pXmlDoc->resolveExternals = VARIANT_FALSE;

        if( pXmlDoc->load("test.xml") != VARIANT_TRUE)

        {

            printf("Fail reason: %s\n", (LPCSTR)pXmlDoc->parseError->Getreason());

       }

        else

       {

            // success

       }

    }

    catch(_com_error errorObject)

    {

        printf("Exception, HRESULT = 0x%08x", errorObject.Error());

    }

 

    上面代碼中,開始3句是設置IXMLDOMDocument接口的3個屬性值。

    async表示調用的阻塞模式,爲true時爲異步,此時load函數調用當即返回,而無論文件加載是否完成;爲false時爲同步模式,即在加載完以後函數返回。在異步模式中,能夠經過查詢readyState屬性值來判斷是否加載完畢,也能夠設置onreadystatechange handler或者onreadystatechange event進行處理。async的默認值爲true。

    validateOnParse表示當xml文件結構有錯誤時是否繼續進行分析,默認值爲true。

    resolveExternals表示在分析xml時,外部定義或document type definition(DTD)等是否被處理,MSXML6.0中的默認值爲false。

    另外要解釋一下VARIANT類型,通常在COM中用的比較多。VARIANT類型被用來表示多種數據類型,在接口中應用仍是很方便的。其實它的定義是一個結構體,其中有一個變量指示了數據的真正類型,還有一個union變量,由各類類型的數據成員構成。這樣,VARIANT就能支持各類類型的數據了。值得一提的是,VARIANT中字符串類型是用BSTR表示的,BSTR也是COM編程中通用的字符串類型,爲Unicode字符串。BSTR字符串的內存分配都由系通通一管理,經過SysAllocString和SysFreeString控制。Windows提供了專門的類來處理VARIANT和BSTR,具體能夠參考這篇文章:http://www.vckbase.com/document/viewdoc/?id=1096

    load函數既能夠加載本地文件,也能夠加載URL形式的遠程文件(沒有測試)。另外還有一個對應的loadXML函數能夠直接加載字符串形式的xml,但只支持UTF-16和UCS-2兩種編碼。

 

    保存xml文件的代碼爲:

 

    try

    {   

          if( FAILED( pXmlDoc->save(L"myData.xml")))

        {

              printf("Fail reason: %s\n", (LPCSTR)pXmlDoc->parseError->Getreason());

          }

          else

          { 

             // success

          }

    }

    catch(_com_error errorObject)

    {

          printf("Exception, HRESULT = 0x%08x", errorObject.Error());

    }

 

2、獲取root節點指針

    有了IXMLDOMDocument接口指針,就能很方便的獲得root節點接口指針。對於加載xml來講,有3種方式,代碼以下:

 

    MSXML2::IXMLDOMElementPtr pRootNode = pXmlDoc->documentElement;

 

    或

 

    MSXML2::IXMLDOMElementPtr pRootNode;

    pXmlDoc->get_documentElement(&pRootNode);

 

    或

 

    MSXML2::IXMLDOMNodePtr pRootNode, pNode;

    pXmlDoc->get_firstChild(&pRootNode);

    while( pRootNode)

    {

         MSXML2::DOMNodeType type;

         pRootNode->get_nodeType(&type);

         if(type==NODE_ELEMENT)

              break;

         pNode = pRootNode;

         pNode->get_nextSibling(&pRootNode);

    }  

 

    最經常使用的又簡單的方法就是第一種。寫出後兩種方法是想說明兩個問題,後面的操做方法將只介紹最經常使用的方法。

    能夠看到第二種方法並非直接訪問的IXMLDOMDocument接口的屬性值,而是經過函數獲得。對於DOM接口的屬性,通常都有對應的get或put函數來對屬性進行讀寫。

    第三種方法是爲了讓你們再次理解各類類型的node之間的聯繫與區別,咱們能夠看到IXMLDOMDocument和IXMLDOMElement均爲一個IXMLDOMNode,咱們能夠經過遍歷IXMLDOMDocument的子節點獲得root節點。只不過要注意的是,IXMLDOMDocument的get_firstChild返回的節點並不必定就是root,多是一些註釋或空格行之類,咱們須要判斷節點類型。節點類型的種類及說明以下表:

 

種類

意義

子節點類型

父節點類型

NODE_ELEMENT

1

表示一個元素

ProcessingInstruction, Text, Comment, CDATASection, EntityReference, Element

Document, DocumentFragment, EntityReference, Element

NODE_ATTRIBUTE

2

表示元素的屬性

Text ,  EntityReference

NODE_TEXT

3

表示一個標籤的文本

Attribute, DocumentFragment, Element, EntityReference

NODE_CDATA_SECTION

4

表示一個CDATA section

DocumentFragment, EntityReference, Element

NODE_ENTITY_REFERENCE

5

表示實體引用

Element, Text, ProcessingInstruction, Comment, CDATASection, EntityReference

Attribute, DocumentFragment, Element, EntityReference

NODE_ENTITY

6

表示擴展實體

可表示該實體的節點類型

DocumentType

NODE_PROCESSING_INSTRUCTION

7

表示一個操做指示

Document, DocumentFragment, Element, EntityReference

NODE_COMMENT

8

表示註釋

Document, DocumentFragment, Element, EntityReference

NODE_DOCUMENT

9

表示xml文檔

Element, ProcessingInstruction, Comment,  DocumentType

NODE_DOCUMENT_TYPE

10

表示文檔類型聲明,出如今<!DOCTYPE>標籤中

Notation,  Entity

Document

NODE_DOCUMENT_FRAGMENT

11

表示文檔片斷或與文檔

Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference

NODE_NOTATION

12

表示DTD中聲明的表示法

Document

   

    而對於新建的一個xml來講,咱們建立IXMLDOMDocument接口後,調用createElement_x函數建立的第一個節點即爲root節點。

3、查詢XML文檔節點

    這部分屬於「讀」XML文檔並作節點遍歷,因爲擔憂加上實例會佔用過多的篇幅影響閱讀,先在這篇作方法總結,之後有時間再寫一篇「實戰篇」專門寫個實例工程,能夠有更完整的參考代碼。

 

    查詢和遍歷XML文檔的大體步驟:建立IXMLDOMDocument接口對象 -> load加載文檔 -> 獲得root節點 -> 依次遍歷各節點。也能夠經過IXMLDOMDocument接口的selectSingleNode或selectNodes函數分別獲得指定節點或節點集合。

 

一、查詢文檔中指定節點

 

    MSXML2::IXMLDOMNodePtr pNode = pXmlDoc->selectSingleNode(L"root/record");

    if( pRootNode == NULL)

    {

         // fail process

    }

    selectSingleNode函數容許用相似路徑的XPath方式查詢節點,返回第一個符合的節點。

 

二、查詢節點集合

 

    MSXML2::IXMLDOMNodeListPtr pNodeList = pXmlDoc->selectNodes(L"root/record ");

    if( pNodeList == NULL)

   {

          // fail process

    }

 

    與上面方法不一樣的是,selectNodes函數返回的是一個節點接口指針列表。須要說明的是,這兩個函數是IXMLDOMNode接口的函數,所以能夠從任一節點進行這樣的查詢,使用相對調用節點的相對路徑便可。若是經過節點的標籤名來查詢,也可使用getElementsByTagName函數,該函數不如selectNodes功能豐富,但使用起來比較簡單。在IXMLDOMNode和IXMLDOMElement接口中均實現了該函數。

 

    MSXML2::IXMLDOMNodeListPtr pNodeList = pXmlDoc->getElementsByTagName_r("tag name");

     if( pNodeList == NULL)

    {

         // fail process

     }

     int nCount = pNodeList->Getlength();

     pNodeList->reset();

     forint i=0; i<nCount; i++)

     {

         MSXML2::IXMLDOMNodePtr pNode = pNodeList->Getitem(i);

         if(pNode)

         {

              // node process

         }

     }

 

三、查詢節點屬性

    查詢IXMLDOMElement接口節點的某個屬性值:

 

    _variant_t varValue = pRootNode->getAttribute("attirbute name");

     if( varValue.vt != VT_NULL)

     printf("%s", _bstr_t(varValue));

 

    或者能夠先獲得IXMLDOMAttribtute接口,經過接口函數查詢屬性值:

 

    MSXML2::IXMLDOMAttributePtr pAttriNode = pRootNode->getAttributeNode("attirbute name");

    if( pAttriNode)

    {

         _variant_t varValue;

         HRESULT hr = pAttriNode->get_nodeval_rue(&varValue);

         if( SUCCEEDED(hr))

         {  

            printf("%s", _bstr_t(varValue));

         }   

} 

 

    IXMLDOMNode接口類中有attributes成員變量,能夠直接拿到節點屬性的集合,再經過IXMLDOMNamedNodeMap接口查詢屬性值:

 

    MSXML2::IXMLDOMNamedNodeMapPtr pAttrs = pRootNode->Getattributes();

    if( pAttrs) {

         MSXML2::IXMLDOMNodePtr pNode = pAttrs->getNamedItem("attirbute name");

         if( pNode) {

              _variant_t varValue;

              HRESULT hr = pNode->get_nodeval_rue(&varValue);

              if( SUCCEEDED(hr))

                   printf("%s", _bstr_t(varValue));

         }

    }

 

    也能夠經過IXMLDOMNamedNodeMap的元素遍從來查詢。

 

四、查詢節點內容

    從IXMLDOMNode繼承的接口均可以直接查詢節點內容:

 

    _bstr_t bstrText = pNode->Gettext();

    printf("%s", bstrText);

 

    若節點類型是CDATA SECTION,則Gettext函數返回的是CDATA的文本內容;若爲Comment類型則返回註釋內容。

 

五、查詢節點名稱

    對於元素類型節點或者屬性節點,有時須要查詢其標籤名或者屬性名,能夠用IXMLDOMNode接口函數:

 

    _bstr_t bstrName = pNode->GetnodeName();

    printf("%s", bstrName);

 

    注意GetnodeName函數對於不一樣類型的節點獲得的名稱種類是不一樣的,具體可參考MSDN。

 

4、建立或修改XML文檔節點

    這部分屬於「寫」XML文檔,大體的步驟是:建立IXMLDOMDocument接口對象 -> 建立root節點並添加到document上 -> 依次建立所需類型的節點並添加到父節點。對於修改已有XML文檔節點,只須要按照上面查詢節點的方法找到該節點,用get相對應的put函數修改便可。下面主要介紹一下建立的詳細過程。

 

一、建立節點

    下面是document添加root節點的代碼:

 

    MSXML2::IXMLDOMElementPtr pRootNode = pXmlDoc->createElement_x("root");

    pXmlDoc->appendChild(pRootNode);

 

    通常狀況下,建立節點的步驟都是由IXMLDOMDocument接口對象create一個類型節點出來,而後由父節點接口對象調用appendChild函數將建立節點添加上去。總結一下建立各種型節點接口的方法:

       IXMLDOMAttribute             :createAttribute

       IXMLDOMCDATASection          :createCDATASection

       IXMLDOMComment               :createComment

       IXMLDOMDocumentFragment      :createDocumentFragment

       IXMLDOMElement               :createElement_x

       IXMLDOMEntityReference       :createEntityReference

       IXMLDOMProcessingInstruction :createProcessingInstruction

       IXMLDOMText                  :createTextNode

    另外還有一個createNode函數能夠建立指定類型的節點。

 

二、設置建立節點各類類型值

    下面是設置一個節點的內容代碼:

 

    MSXML2::IXMLDOMNodePtr pNode = pXmlDoc->createElement_x("title");

    if( pNode)

    {

         pNode->Puttext("title text");

         pRootNode->appendChild(pNode);

    }

 

    只須要調用各種型接口對應的put函數進行設置就能夠了。

 

三、設置建立節點的屬性

    兩種方法,一種是先添加IXMLDOMElement類型節點再設置屬性:

 

    _variant_t varLanguage = "chinese";

    HRESULT hr = pRootNode->setAttribute("language", varLanguage);

    ASSERT(SUCCEEDED(hr));

 

    另外一種是直接添加IXMLDOMAttribute類型節點:

 

    MSXML2::IXMLDOMAttributePtr pAttribute = pXmlDoc->createAttribute("language");

    if(pAttribute)

    {

         _variant_t varLanguage = "chinese";

         pAttribute->Putvalue(varLanguage);

         pRootNode->setAttributeNode(pAttribute);

    }

 

四、插入節點

    插入節點能夠用insertBefore函數,代碼以下:

 

    MSXML2::IXMLDOMElementPtr pNewElement = pXmlDoc->createElement_x("date");

    if( pNewElement)

    {

         HRESULT hr = pRootNode->insertBefore(pNewElement, (_variant_t)pRootNode->GetchildNodes()->Getitem(1));

         ASSERT(SUCCEEDED(hr));

    }

 

    對於不一樣類型的節點,此函數要求插入的節點類型和返回值類型都有比較複雜的規範,具體能夠參考MSDN,在此不詳細介紹了。

 

五、  刪除節點

    對於不一樣類型的節點接口,有不一樣的remove函數能夠刪除節點,總結以下:

    IXMLDOMElement : removeAttribute,removeAtrributeNode

    IXMLDOMNamedNodeMap : removeNamedItem

    IXMLDOMAttributeIXMLDOMCommentIXMLDOMDocumentIXMLDOMDocumentFragmentIXMLDOMElementIXMLDOMNodeIXMLDOMText  : removeChild

 

    示例代碼:

 

    MSXML2::IXMLDOMElementPtr pRootNode = pXmlDoc->documentElement;

    pRootNode->removeAttribute("languge");

 

    這篇就總結這些,但願對你們有所幫助;若是有寫的不對之處,請不吝賜教。

相關文章
相關標籤/搜索