胡朝晖 摘自:计算机世界
---- XML DOM (文档对象模型)对象提供了一个标准的方法来操作存储在XML文档中的信息,
这就是DOM应用编程接口(API)函数。它是应用程序和XML文档之间的桥梁。DOM包 含两个关键的抽象概念:一个是树状的层次结构,另一个是用来表示文档内容和结构的节点
集合。树状层次包括了所有节点,节点本身也可以包含其他的节点。这样的好处是可以通过 这个层次结构来找到并修改某一特定节点的信息。 ----微软的MSXML解析器读取一个XML文档,然后把它的
内容解析到一个抽象的信息容器中,该信息容器被称为节点(NODES)。这些节点代表文 档的结构和内容,并允许应用程序来操作文档中的信息而不需要知道XML的语义。一个文
档被解析后,它的节点能够在任何时候被浏览而不需要保持一定的顺序。 ----对开发人员来说,最重要的编程对象是DOMDocument。
DOMDocument对象通过暴露的属性和方法来允许浏览、查询和修改XML文档的内容和结 构。 ----本文主要介绍DOM的结构和应用,同时用VC编程语言
给出了通过MSXML进行XML解析的实例。 DOMDocument对象的结构和应用 ----文档对象的创建
HRESULT hr; IXMLDomDocument* pXMLDoc; IXMLDOMNode*
pXDN; //COM的初始化 Hr=CoInitialize(NULL); /*得到关于IXMLDOMDocument 接口的指针pXMLDOC*/
hr=CoCreateInstance(CLSID_DOMDocument,NULL, CLSCTX_INPPROC_SERVER,IID_IXMLDOMDocument,
(void**)&pXMLDoc); //得到关于IXMLDOMNode接口的指针pXDN hr=pXMLDoc->QueryInterface(IID_IXMLDOM Node,(void**)&pXDN);
----在MSXML解析器的使用过程中,我们可以使用文档中
的CreateElement方法创建一个节点来装载和保存XML文件,也可以通过Load或者是 LoadXML方法从一个指定的URL来装载一个XML文档。Load(LoadXML)方法带有两个参
数:第一个参数xmlSource表示需要被解析的文档,第二个参数isSuccessful表示文档装载 是否成功。 ----文档对象的保存
----Save方法是用来把文档保存到一个指定的位置。Save方 法中参数destination用来表示需要被保存的对象的类型,对象可以是一个文件、一个ASP
Response方法、一个XML文档对象,或者是一个能够支持持久保存(persistence)的客户 对象。下面是使用Save方法的一个例子程序的部分代码:
BOOL DOMDocSaveLocation() { BOOL bResult
= FALSE; IXMLDOMDocument *pIXMLDOMDocument = NULL; HRESULT hr; try
{ _variant_t varString = _T(“D:\\sample.xml"); /* 这里省略了创建一个DOMDocument
对象和装载XML文档的代码*/ //将文档保存到D:\\sample.xml中去 hr=pIXMLDOMDocument->save(varString);
if(SUCCEEDED(hr)) bResult = TRUE; } catch(...) { DisplayErrorToUser();
/*这里省略了释放对IXMLDOMDocument 接口的引用的代码*/ } return bResult; }
----设置解析标志
----在解析过程中,我们需要得到和设置解析标志。利用不 同的解析标志,我们可以用不同的方法来解析一个XML文档。XML标准允许解析器验证
或者不验证文档,允许不验证文档的解析过程跳过对外部资源的提取,还可以设置标志来表 明是否要从文档中移去多余的空格。DOMDocument对象暴露了如下几个属性,允许用户在
运行的时候利用它们改变解析器的行为。 Async属性方法:get_Async和put_Async。 ValidateOnParse属性方法:get_ValidateOnParse和
put_ValidateOnParse。 ResolveExternals属性方法:get_ ResolveExternals和put_
ResolveExternals。 PreserveWhiteSpace属性方法:get_ PreserveWhiteSpace和put_
PreserveWhiteSpace。 ----每一个属性可以接受或者返回一个Boolean值。缺省情况
下,Async、ValidateOnParse、ResolveExternals的值为TRUE,PreserveWhiteSpace的值跟 XML文档的设置有关,如果XML文档中设置了xml:space属性的话,该值为FALSE。
----在文档解析过程中可以收集到以下的信息: doctype(文档类型):是用来定义文档格式的DTD文件。如果XML文档没有相关的
DTD文档的话,它就返回NULL。 implementation(实现):表示该文档的实现,用来指出当前文档所支持的XML的版本。
parseError(解析错误):指出在解析过程中最后所发生的错误。 readyState(状态信息):表示XML文档的状态信息。readyState对于异步使用微软的XML
解析器来说重要的作用是提高了性能。当异步装载XML文档的时候,程序可能需要检查解 析的状态,MSXML提供了4个状态,分别为正在状态、已经状态、正在解析和解析完成。
url(统一资源定位):表示正在被装载和解析的XML文档的URL的情况。如果该文档是 在内存中建立的话,这个属性返回NULL值。 ----节点的操作
---- 在得到文档树结构以后,我们可以操作树中的每一个节 点,一般通过两个方法得到树中的节点,分别为nodeFromID和getElementsByTagName。
---- nodeFromID包括两个参数,第一个参数idString用来表 示ID值,第二个参数node返回指向和该ID相匹配的节点的接口指针。根据XML的技术规
定,每一个XML文档中的ID值必须是唯一的,而且一个元素(element)只能和一个ID 相关联。 ----getElementsByTagName方法有两个参数,第一个参数
tagName表示需要查找的元素(Element)名称,如果tagName为“*”则返回文档中所有的元 素。第二个参数为resultList,它实际是指向接口IXMLDOMNodeList的指针,用来返回和
tagName(标签名字)相关的所有节点的集合。 ----下面是相关例子程序的部分代码: IXMLDOMDocument
*pIXMLDOMDocument = NULL; wstring strFindText (_T(“author")); IXMLDOMNodeList
*pIDOMNodeList = NULL; IXMLDOMNode *pIDOMNode = NULL; long value; BSTR
bstrItemText; HRESULT hr; try { /*此处省略创建一个DOMDocument 文档对象并装载具体文档的代码*/ /*下面的代码用来得到一个和标签名称
author相关的所有节点的集合*/ //是否正确地得到了指向IDOMNodeList的指针 hr=pIXMLDOMDocument->getElementsByTagName
((TCHAR*)strFindText.data(), &pIDOMNodeList); SUCCEEDED(hr) ? 0 : throw hr;
//得到所包含的节点的个数 hr = pIDOMNodeList->get_length(&value); if(SUCCEEDED(hr))
{ pIDOMNodeList->reset(); for(int ii = 0; ii < value; ii++)
{ //得到具体的一个节点 pIDOMNodeList->get_item(ii,&pIDOM Node); if(pIDOMNode
) { //得到该节点相关的文本信息 pIDOMNode->get_text(&bstrItemText); ::MessageBox(NULL,
bstrItemText, strFindText.data(), MB_OK); pIDOMNode->Release();
pIDOMNode = NULL; } } } pIDOMNodeList->Release(); pIDOMNodeList
= NULL; } catch(...) { if(pIDOMNodeList) pIDOMNodeList->Release();
if(pIDOMNode) pIDOMNode->Release(); DisplayErrorToUser(); }
----可以通过方法CreateNode来创建一个新的节点。
CreateNode包括4个参数,第一个参数type表示要创建的节点的类型,第二个参数name表 示新节点的nodeName的值,第三个参数namespaceURI表示和该节点相关的名字空间,第
四个参数node表示新创建的节点。可以通过使用已经提供的类型(type)、名称(name)和名字 空间(nodeName)来创建一个新节点。 ----当一个节点被创建的时候,它实际上是在一个名字空间
范围(如果已经提供了名字空间的话)内被创建的。如果没有提供名字空间的话,它实际上 是在文档的名字空间范围内被创建的。 解析XML
----为了说明如何在VC中使用XML DOM模型,这里我们 介绍一个简单的Console Application实例程序。下面是主要的程序代码,用来在一个XML
文档中定位一个特殊的节点,并插入一个新的子节点。 #include /*下面的.h文件是在安装了最新的
XML Parser以后所包含的.h文件*/ #include “C:\Program Files\Microsoft XML Parser
SDK\inc\msxml2.h" #include void main() { // 初始化COM接口 CoInitialize(NULL); /*在程序中,假定装载的XML文件名称为 xmldata.xml,缺省情况下它和可执行文件在同
一个目录中。该文件的内容如下: <?xml version=“1.0"?> Hello, World! ----程序将寻找名为“xmlnode”的节点,插入一个新的名
称为“xmlchildnode”的节点,然后它再去寻找一个名为“xmltext”的节点,然后提取包 含在该节点中的文本并显示它,最后它把新的改变过的XML文档保存在名称为
“updatexml.xml”的文档中。*/ try { // 通过智能指针创建一个解析器的实例
CComPtrspXMLDOM; HRESULT hr =spXMLDOM.CoCreateInstance (-uuidof(DOMDocument));
if ( FAILED(hr) ) throw “不能创建XML Parser对象"; if ( spXMLDOM.p == NULL )
throw “不能创建XML Parser对象";
// 创建成功,开始装载XML文档
VARIANT_BOOL bSuccess = false; hr =spXMLDOM->load(CComVariant( L“xmldata.xml"),&bSuccess);
if ( FAILED(hr) ) throw “不能够在解析器中装载XML文档"; if ( !bSuccess ) throw
“不能够在解析器中装载XML文档"; // 检查并搜索“xmldata/xmlnode" CComBSTR bstrSS(L“xmldata/xmlnode");
CComPtrspXMLNode; /*用接口IXMLDOMDocument的 selectSingleNode方法定位该节点。*/
hr =spXMLDOM->selectSingleNode (bstrSS,&spXMLNode); if ( FAILED(hr)
) throw “不能在XML节点中定位‘xmlnode’"; if ( spXMLNode.p == NULL ) throw
“不能在XML节点中定位‘xmlnode' "; /*DOM对象“spXMLNode” 现在包含了XML节点, 所以我们可以在它下面创建一个子节点。*/
CComPtr spXMLChildNode; /*用接口IXMLDOMDocument的方法create Node方法创建一个新节点。*/
hr = spXMLDOM->createNode( CComVariant(NODE_ELEMENT), CComBSTR(“xmlchildnode"),
NULL,&spXMLChildNode); if ( FAILED(hr) ) throw “不能创建 ‘xmlchildnode' 节点";
if ( spXMLChildNode.p == NULL ) throw “不能创建‘xmlchildnode' 节点"; //添加新节点到spXMLNode节点下
CComPtr spInsertedNode; hr =spXMLNode->appendChild (spXMLChildNode,&spInsertedNode);
if ( FAILED(hr) ) throw “不能创建‘xmlchildnode' 节点"; if ( spInsertedNode.p
== NULL ) throw “不能移动‘xmlchildnode' 节点"; //设置新节点属性 CComQIPtr
spXMLChildElement; spXMLChildElement = spInsertedNode; if ( spXMLChildElement.p
== NULL ) throw “不能在XML元素接口中查询到 ‘xmlchildnode' "; hr =spXMLChildElement->setAttribute (CComBSTR(L“xml"),CComVariant(L“fun"));
if ( FAILED(hr) ) throw“不能插入新的属性"; /*下面的程序段用来寻找一个节点 并显示该节点的相关信息。*/
// 查找“xmldata/xmltext"节点 // 释放先前的节点 spXMLNode = NULL; bstrSS
= L“xmldata/xmltext"; hr =spXMLDOM->selectSingleNode (bstrSS,&spXMLNode);
if ( FAILED(hr) ) throw “不能定位 ‘xmltext'节点"; if ( spXMLNode.p == NULL
) throw “不能定位‘xmltext'节点"; // 得到该节点包含的文本并显示它 CComVariant varValue(VT_EMPTY);
hr =spXMLNode->get_nodeTypedValue (&varValue); if ( FAILED(hr) ) throw
“不能提取‘xmltext'文本"; if ( varValue.vt == VT_BSTR ) { /*显示结果。注意这里要把字符串
从形式BSTR转化为ANSI。*/ USES_CONVERSION; LPTSTR lpstrMsg = W2T (varValue.bstrVal);
std::cout<< lpstrMsg << std::endl; } // if else { // 如果出现错误
throw “不能提取‘xmltext'文本"; } // else //将修改过的XML文档按指定的文档名保存 hr =
spXMLDOM->save(CComVariant (“updatedxml.xml")); if ( FAILED(hr)
) throw “不能保存修改过的XML文档"; std::cout << “处理完成..." <<
std::endl << std::endl; } // try catch(char* lpstrErr) { //
出现错误 std::cout << lpstrErr << std::endl << std::endl;
} // catch catch(...) { // 未知错误 std::cout << “未知错误..."
<< std::endl << std::endl; } // catch // 结束对COM的使用 CoUninitialize(); } ----因为XML文档有比HTML更加严格的语法要求,所以使
用和编写一个XML解析器比编写一个HTML的解析器要容易。同时因为XML文档不仅标 记文档的显示属性,更重要的是它标记了文档的结构和包含信息的特征,所以我们可以方便
地通过XML解析器来获取特定节点的信息并加以显示或修改。 推荐本文给好友 我要投稿>>
进入信息化BBS论坛
|