XML解析之 MSXML應用總結 開發篇(上)

(由於包含太多格式符號,新浪提示篇幅過長,因此分爲上、下兩篇)

   本篇是接前文“MSXML應用總結 概念篇”寫的,主要總結一下MSXML DOM接口的應用。DOM(Document Object Model)是微軟提供的處理XML文檔的一個API標準庫,我們可以將其理解爲一組抽象了XML文檔結構的接口。

 

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

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

   首先我們看一下常用的接口:

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

   IXMLDOMNode:節點接口,該接口是普遍意義上的節點接口,很多類型節點接口都從它派生,包括IXMLDOMDocument;

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

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

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

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

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

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

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

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

 

                                                             

在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文件進行操作了,我將按操作分類進行總結。

   

一、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());
   }

 

二、獲取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節點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章