使用 MSXML 分析器處理 XML 文檔發(fā)布日期: 8/6/2004 | 更新日期: 8/6/2004
Kenn Scribner 在 Kenn Scribner 近期有關(guān) XML 和 MSXML DOM 分析器的文章中,僅介紹了該分析器的部分功能。這些文章將 XML 作為一種技術(shù)進行了說明,但是并沒有介紹 XML 分析器本身?,F(xiàn)在,Kenn 將回過頭來介紹 MSXML 分析器,并講解處理 XML 文檔和節(jié)點所需的基本知識:搜索特定的節(jié)點、插入節(jié)點和檢索節(jié)點值。 MSXML 分析器基于 XML 文檔對象模型,對于查看表 1 中所示的各種文檔對象來說,它非常重要。這些對象直接出自 XML 規(guī)范本身。MSXML 還可以進一步將 XML DOM 對象合并到 COM 中。因此,弄清楚哪個 XML DOM 對象對應(yīng)于哪個 MSXML COM 接口非常容易。例如,IXMLDOMNode 代表稱為 Node 的 DOM 對象。
雖然有時比較容易混淆,但是 XML 文檔對象可以是(并且通常是)多態(tài)的。即,“節(jié)點”同時也是一個“元素”。當您試圖確定需要何種 DOM 對象來執(zhí)行何種操作時,這有時會造成混淆??梢允褂?#8220;文檔”對象來創(chuàng)建 DOM“節(jié)點”,但是,如果要向新創(chuàng)建的節(jié)點添加屬性,就必須通過其作為“元素”的一面來訪問它。如果說存在一種將對象和操作關(guān)聯(lián)在一起的神奇模式,那么我還沒能從自己的日常工作中將它提煉出來。我發(fā)現(xiàn)自己仍需要不斷參考 MSDN 文檔來查看哪個 COM 接口提供了所需的方法以執(zhí)行我試圖完成的任務(wù)。各種對象方法看上去的確是按邏輯分組的,這也正是我對 DOM 當初的開發(fā)模式的推斷(通過分組邏輯操作)。 因此,其中的訣竅就在于從 MSXML 分析器檢索適當?shù)?DOM 對象,這一操作的具體實現(xiàn)就是 COM 對象。操作的基本模式將是:首先實例化 MSXML COM 對象本身的一個副本,然后從該副本請求或以其他方式獲取指向附加 XML DOM 對象(本身也是 COM 對象)的指針。 MSXML DOM 試驗應(yīng)用程序創(chuàng)建一個漂亮的應(yīng)用程序,演示眾多的 MSXML 功能,這很簡單,但實際上,附加的代碼只會畫蛇添足。相反,我選擇了開發(fā)一個簡單的基于控制臺的應(yīng)用程序,該應(yīng)用程序執(zhí)行四種基本操作:
為了進一步簡化,我硬編碼了 XML 文檔文件的名稱和 XML 節(jié)點本身。當然,如果這是一個真實的應(yīng)用程序,您可能很少(或者永遠不會)采用這樣的方法。但是在本例中,進行這些權(quán)衡,是為了簡化圍繞在 MSXML 功能兩邊的代碼。 像平常一樣,在示例應(yīng)用程序中,我選擇了使用 ATL 來包裝許多與 COM 有關(guān)的活動。您肯定看到我使用了 CComPtr 和 CComQIPtr 對象,但是我還額外加入了幾個 CComBSTR 和 CComVariant 對象。如果您不熟悉它們,只需要記住它們是用于處理一些細節(jié)的模板,這些細節(jié)對于本文的主旨來說并非至關(guān)重要,但是從更廣的角度講,還是比較重要的。真正重要的是看到如何搜索 XML 節(jié)點,添加新的(具有屬性的)節(jié)點,以及顯示節(jié)點內(nèi)包含的文本。 我的基于控制臺的應(yīng)用程序可以在附帶的 下載文件中找到,它將加載一個名為 xmldata.xml 的 XML 文檔文件(假定其與可執(zhí)行文件位于同一個目錄中),并假定該文檔包含以下 XML 數(shù)據(jù): <?xml version="1.0"?> <xmldata> <xmlnode /> <xmltext>Hello, World!</xmltext> </xmldata> 我們將首先搜索 xmlnode 節(jié)點,如果找到了該節(jié)點,我們將插入一個新的(帶有屬性的)節(jié)點作為其子級。生成的 XML 文檔將為: <?xml version="1.0"?>
<xmldata>
<xmlnode>
<xmlchildnode xml="fun" />
</xmlnode>
<xmltext>Hello, World!</xmltext>
</xmldata>
打印 節(jié)點內(nèi)包含的信息 ("Hello, World!") 之后,我們將把該新 XML 文檔保存到名為 updatedxml.xml 的文件中。然后,就可以使用文本編輯器或 Internet Explorer 5.x 來查看結(jié)果?,F(xiàn)在讓我們轉(zhuǎn)到代碼。 應(yīng)用程序首先初始化了 COM 運行庫,然后創(chuàng)建了 MSXML 分析器的一個實例: CComPtr<IXMLDOMDocument> spXMLDOM;
HRESULT hr = spXMLDOM.CoCreateInstance(
__uuidof(DOMDocument));
if ( FAILED(hr) )
throw "Unable to create XML parser object";
if ( spXMLDOM.p == NULL )
throw "Unable to create XML parser object";
如果創(chuàng)建分析器實例成功,接下來,我們將把 XML 文檔加載到分析器中: VARIANT_BOOL bSuccess = false;
hr = spXMLDOM->load(CComVariant(L"xmldata.xml"),
&bSuccess);
if ( FAILED(hr) )
throw "Unable to load XML document into the parser";
if ( !bSuccess )
throw "Unable to load XML document into the parser";
搜索節(jié)點與文檔對象有關(guān),因此,我們將使用 IXMLDOMDocument::selectSingleNode() 來根據(jù)其名稱查找特定的 XML 節(jié)點。其他的技巧很多,但是如果準確地知道要查找的節(jié)點的名稱,這是最直接的方法: CComBSTR bstrSS(L"xmldata/xmlnode"); CComPtr<IXMLDOMNode> spXMLNode; hr = spXMLDOM->selectSingleNode(bstrSS,&spXMLNode); if ( FAILED(hr) ) throw "Unable to locate ‘xmlnode‘ XML node"; if ( spXMLNode.p == NULL ) throw "Unable to locate ‘xmlnode‘ XML node"; 一些您應(yīng)當了解的其他方法包括 IXMLDOMDocument::nodeFromID() 和 IXMLDOMElement::getElementsByTagName(),使用它們可以獲得文檔中的節(jié)點的列表。您還可以將文檔作為樹來進行訪問,并依次通過它(獲取子節(jié)點,獲取同輩節(jié)點等)。 任一種情況下,搜索的結(jié)果都是一個 MSXML 節(jié)點對象 IXMLDOMNode。文檔中必須存在該節(jié)點,否則搜索將失敗。我的應(yīng)用程序使用該節(jié)點作為一個全新 XML 節(jié)點的父級,該新節(jié)點是由 XML 文檔對象創(chuàng)建的: CComPtr<IXMLDOMNode> spXMLChildNode;
hr = spXMLDOM->createNode(CComVariant(NODE_ELEMENT),
CComBSTR("xmlchildnode"),
NULL,
&spXMLChildNode);
if ( FAILED(hr) )
throw "Unable to create ‘xmlchildnode‘ XML node";
if ( spXMLChildNode.p == NULL )
throw "Unable to create ‘xmlchildnode‘ XML node";
如果分析器可以創(chuàng)建該節(jié)點,下一步就是將它放到 XML 樹中。IXMLDOMNode::appendChild() 正是完成這一任務(wù)的方法: CComPtr<IXMLDOMNode> spInsertedNode;
hr = spXMLNode->appendChild(spXMLChildNode,
&spInsertedNode);
if ( FAILED(hr) )
throw "Unable to move ‘xmlchildnode‘ XML node";
if ( spInsertedNode.p == NULL )
throw "Unable to move ‘xmlchildnode‘ XML node";
如果父節(jié)點的確將新創(chuàng)建的節(jié)點插入為其子級,將返回另一個 IXMLDOMNode 實例,該實例表示新的子節(jié)點。實際上,該新子節(jié)點和傳遞給 appendChild() 的節(jié)點是同一個 XML 節(jié)點。由于在存在問題時附加的子節(jié)點的指針將為 Null,因此,檢查該指針很有用。 到目前為止,我找到了一個特定的節(jié)點,并為它創(chuàng)建了一個新的子節(jié)點,下面,讓我們看看如何處理屬性。假定您要將該屬性添加到新的子節(jié)點: xml="fun" 這并不難,但是您必須從 IXMLDOMNode 切換到 IXMLDOMElement,以便訪問該子節(jié)點的元素特征。在實踐中,這意味著您必須查詢 IXMLDOMNode 接口的相關(guān) IXMLDOMElement 接口,查明后,再調(diào)用 IXMLDOMElement::setAttribute(): CComQIPtr<IXMLDOMElement> spXMLChildElement;
spXMLChildElement = spInsertedNode;
if ( spXMLChildElement.p == NULL )
throw "Unable to query for ‘xmlchildnode‘ XML _
element interface";
hr = spXMLChildElement->setAttribute(CComBSTR(L"xml"),
CComVariant(L"fun"));
if ( FAILED(hr) )
throw "Unable to insert new attribute";
此時,已經(jīng)修改了 XML 樹,并創(chuàng)建了所需的樹。應(yīng)用程序可以在這個時候?qū)⑽臋n保存到磁盤,或者執(zhí)行其他任務(wù)?,F(xiàn)在,讓我們來搜索另一個節(jié)點并顯示該節(jié)點所包含的值(文本)。您已經(jīng)了解了如何搜索節(jié)點,因此,我們將直接講解數(shù)據(jù)提取。 提取節(jié)點數(shù)據(jù)的關(guān)鍵在于使用 IXMLDOMNode::get_nodeTypedValue()??梢允褂?Microsoft 數(shù)據(jù)類型架構(gòu)來標識節(jié)點所包含的數(shù)據(jù),因此可以方便地存儲浮點值、整數(shù)、字符串或該架構(gòu)所支持的任何數(shù)據(jù)類型??梢允褂?dt:type 屬性來指定數(shù)據(jù)類型,如下所示: <model dt:type="string">SL-2</model> <year dt:type="int">1992</year> 如果特定的節(jié)點具有指定的數(shù)據(jù)類型,就可以使用 get_nodeTypedValue() 以該格式提取數(shù)據(jù)。如果未指定數(shù)據(jù)類型,將假定數(shù)據(jù)為文本,分析器將返回具有 BSTR 數(shù)據(jù)的 VARIANT。在本例中,這沒有任何問題,因為我們要搜索的節(jié)點是一個實際上包含一個字符串的文本節(jié)點。在需要時,始終可以使用 atoi() 等方法將字符串轉(zhuǎn)換為其他形式。本例中,我們只是提取該字符串數(shù)據(jù)并顯示它: CComVariant varValue(VT_EMPTY);
hr = spXMLNode->get_nodeTypedValue(&varValue);
if ( FAILED(hr) )
throw "Unable to retrieve ‘xmltext‘ text";
if ( varValue.vt == VT_BSTR ) {
// Display the results...since we‘re not using the
// wide version of the STL, we need to convert the
// BSTR to ANSI text for display...
USES_CONVERSION;
LPTSTR lpstrMsg = W2T(varValue.bstrVal);
std::cout << lpstrMsg << std::endl;
}
else {
// Some error
throw "Unable to retrieve ‘xmltext‘ text";
}
如果能夠檢索與節(jié)點關(guān)聯(lián)的值,并且該值為 BSTR(預(yù)期的數(shù)據(jù)類型),我們將在屏幕上顯示該文本。如果不能,將顯示一條錯誤消息,不過,根據(jù)情況而定,可以方便地采取其他操作。 最后一項與 XML 有關(guān)的操作是將已更新的 XML 樹保存到磁盤,這一任務(wù)是使用 IXMLDOMDocument::save() 完成的: hr = spXMLDOM->save(CComVariant("updatedxml.xml"));
if ( FAILED(hr) )
throw "Unable to save updated XML document";
完成保存后,向屏幕寫一條簡短說明,并退出。 這個示例應(yīng)用程序無論如何都算不上漂亮。您可以讓自己的應(yīng)用程序執(zhí)行很多其他功能,但我希望您通過這個簡短的示例了解到了如何從 C++ 程序使用 MSXML 分析器。該分析器本身是一個復(fù)雜的軟件,無論怎樣強調(diào)使用 MSDN Library 作為參考,都不能算是過份。該分析器公開了許多接口,這些接口通常會公開許多方法。即便如此,我在自己的項目中仍頻繁地使用該分析器,在親自編寫了一些代碼并進行試驗后,我發(fā)現(xiàn)這個軟件制作很精良 并且便于使用。我希望您也同樣會發(fā)現(xiàn)該分析器和一般意義上的 XML 具有廣泛的用途。 要了解有關(guān) Visual C++ Developer 和 Pinnacle Publishing 的更多信息,請訪問他們的 Web 站點,網(wǎng)址為: http://www./ 注:這不是 Microsoft Corporation 的網(wǎng)站。Microsoft 對該網(wǎng)站內(nèi)容不承擔(dān)責(zé)任。 本文復(fù)制自 Visual C++ Developer 的 2000 年 11 月刊。版權(quán)所有 2000,Pinnacle Publishing, Inc.(除非另行說明)。保留所有權(quán)利。Visual C++ Developer 是 Pinnacle Publishing, Inc. 獨立發(fā)行的產(chǎn)品。未經(jīng) Pinnacle Publishing, Inc. 事先同意,不得以任何形式使用或復(fù)制本文的任何部分(評論文章中的簡短引用除外)。要聯(lián)系 Pinnacle Publishing, Inc.,請致電 1-800-788-1900。 |
||||||||||||||||||||||||||||||||||||||||||
|
|
來自: shaolong007 > 《XML》