利用MSXML解析XML文本(1)
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,預設情況下它和可執行檔在同
一個目錄中。該檔案的內容如下:
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解析器來擷取特定節點的資訊並加以顯示或修改。
下面是我最近做的一個程式的儲存xml格式:存和讀取
存的時候用的SAX2.0 讀取的時候用的DOM
void CSysDrawDoc::CreateXmlDoc(CFile *pfile)
{
HRESULT hr;
hr = CoInitialize(NULL);
USES_CONVERSION;
ISAXDTDHandlerPtr dtd=NULL;
hr = pWr.CreateInstance(__uuidof(MXXMLWriter40));
dtd = pWr;
pWr->indent = VARIANT_TRUE;
pWr->omitXMLDeclaration =VARIANT_TRUE;
ISAXContentHandlerPtr spContentHandler=NULL;
spContentHandler = pWr;
//Start the document by adding the XML declaration.
hr = spContentHandler->startDocument ();
char szPI[]= "version='1.0' encoding='gb2312' standalone='no'";//iso-8859-1 encoding = 'utf-16'
//Step 1: Create the XML declaration.
hr = spContentHandler->processingInstruction(L"xml", 3, A2W(szPI), strlen(szPI));//xml版本聲明
// 沒有加DTD驗證
// ISAXLexicalHandlerPtr lexh;
// lexh = pWr;
// lexh->startDTD(L"svg",3,L"-//W3C//DTD SVG 20001102//EN",28,L"http://www.w3.org/TR/2000/CR-SVG-20001102.dtd",45);
// lexh->endDTD();
// Step 2: Create the root ---svg element.
IMXAttributesPtr pMXAttr = NULL;
ISAXAttributesPtr spAttributes = NULL;
hr = pMXAttr.CreateInstance(__uuidof(SAXAttributes40));
pMXAttr->addAttribute(L"",L"Width",L"Width","float","3200");
pMXAttr->addAttribute(L"",L"Height",L"Height","float","2400");
pMXAttr->addAttribute(L"",L"xmlns",L"xmlns","string","http://www.w3.org/2000/svg");
spAttributes= pMXAttr;
hr = spContentHandler->startElement(L"", 0, L"svg",3, L"svg",3,spAttributes);
//Step 3: 添加子類元素
int num = 0;
for (POSITION pos = m_objects.GetHeadPosition(); pos != NULL; )
{
CDrawObj* pObj = m_objects.GetNext(pos);
// if((!pObj->IsKindOf(RUNTIME_CLASS(CDrawE_Label)))&&(!pObj->IsKindOf(RUNTIME_CLASS(CDrawE_Node))))
{
if(!pObj->m_bElemGroup)
{
pObj->WriteCont(pWr);
VARIANT vtTemp = pWr->output;
CString str= vtTemp.bstrVal;
LPTSTR st = str.LockBuffer();
int len = str.GetLength();
pfile->Write(st,len);
vtTemp.vt =VT_EMPTY;
pWr->output = vtTemp;
}
}
}
//結束根項目,和文檔
hr = spContentHandler->endElement(L"",0,L"svg",3, L"svg",3);
hr = spContentHandler->endDocument();
VARIANT vtTemp = pWr->output;
CString str ="";
str= vtTemp.bstrVal;
LPTSTR st = str.LockBuffer();
int len = str.GetLength();
pfile->Write(st,len);
vtTemp.vt =VT_EMPTY;
pWr->output = vtTemp;
}
void CSysDrawDoc::ReadDOMDoc(char *p)
{
CSysDrawApp *pApp = (CSysDrawApp *)AfxGetApp();
HRESULT hr;
CoInitialize(NULL);
hr = pXMLDoc.CreateInstance(__uuidof(DOMDocument40));
if(FAILED(hr))
return;
CString m_sys = p;
_variant_t varOut((bool)TRUE);
_variant_t varXml = m_sys;
varOut = pXMLDoc->load(varXml);
if((bool)varOut == FALSE )
{
AfxMessageBox("文檔格式不正確,不能夠載入xml文檔");
return;
}
// if ((bool)varOut == FALSE)//出錯資訊
// {
// IXMLDOMParseErrorPtr errPtr = pXMLDoc->GetparseError();
// _bstr_t bstrErr(errPtr->reason);
// _tprintf(_T("Error:\n"));
// _tprintf(_T("Code = 0x%x\n"), errPtr->errorCode);
// _tprintf(_T("Source = Line : %ld; Char : %ld\n"), errPtr->line, errPtr->linepos);
// _tprintf(_T("Error Description = %s\n"), (char*)bstrErr);
// }
IXMLDOMNodePtr pNode = NULL;
IXMLDOMNodeListPtr pNodeList = NULL;
IXMLDOMElementPtr pDOMElement = NULL;
IXMLDOMNamedNodeMapPtr pAttributeMap = NULL;
IXMLDOMNodePtr pChildListNode = NULL;
// IXMLDOMNodeList *pIXMLDOMNodelist = NULL;
// IXMLDOMNamedNodeMapPtr pAttributeMap = NULL;
IXMLDOMNodePtr pAttributeNode = NULL;
hr = pXMLDoc->get_documentElement(&pDOMElement);
if(pDOMElement == NULL)
{
AfxMessageBox("文檔解析失敗!");
return;
}
pDOMElement->get_childNodes(&pNodeList);
long num =pNodeList->length ;
for (int i = 0;i < num ;i++)
{
pNodeList->get_item(i, &pNode);
if(pNode)
{
//如果接點不為空白,解析類子項目接點line,bianyaqi
_bstr_t str = pNode->nodeName;//root的子項目名稱
_bstr_t name = _T("name");
BSTR bstrAttributeName =name;
IXMLDOMNode *pIXMLDOMNode = NULL;
VARIANT varValue;
pNode->get_attributes(&pAttributeMap);
long length = pAttributeMap->length; //
/* char group[10];
BOOL bgroup = FALSE;
hr = pAttributeMap->get_item(1,&pIXMLDOMNode);
if(pIXMLDOMNode)
{
_bstr_t s = pIXMLDOMNode->nodeName;
pIXMLDOMNode->get_nodeValue(&varValue);
strcpy(group,_bstr_t(&varValue));
if(pApp->StringToBool(group))
bgroup = TRUE;
}*/
hr = pAttributeMap->get_item(0,&pIXMLDOMNode);//返回name的屬性值
if(pIXMLDOMNode)
{
_bstr_t s = pIXMLDOMNode->nodeName;
pIXMLDOMNode->get_nodeValue(&varValue);
char strin[30];
strcpy(strin, _bstr_t(varValue) );
CONT_TYPE step;
int i = arrayicmp(strin, objcmd);
............省略
}
希望對樓主有所協助,
}
}