?。?、DOM樹(shù)
所有類(lèi)型的XML解析器都要求處理對象是“格式良好”的XML文檔,有些還能根據DTD或XML Schema進(jìn)行有效性驗證,DOM(Document Object Model)解析器將XML文檔一次性解析,生成一個(gè)位于內存中的對象樹(shù)用以描述該文檔。
DOM是一種與平臺和語(yǔ)言無(wú)關(guān)的接口,它允許程序和腳本動(dòng)態(tài)訪(fǎng)問(wèn)和修改文檔的內容、結構和類(lèi)型。它定義了一系列的對象和方法對DOM樹(shù)的節點(diǎn)進(jìn)行各種隨機操作:
● Document對象:作為樹(shù)的最高節點(diǎn),Document對象是對整個(gè)文檔進(jìn)行操作的入口。
● Element和Attr對象:這些節點(diǎn)對象都是文檔某一部分的映射,節點(diǎn)的定級層次恰好反映了文檔的結構。
● Text對象:作為Element和Attr對象的子節點(diǎn),Text對象表達了元素或屬性的文本內容。Text節點(diǎn)不再包含任何子節點(diǎn)。
● 集合索引:DOM提供了幾種集合索引方式,可以對節點(diǎn)按指定方式進(jìn)行遍歷。索引參數都是從0開(kāi)始記數的。
DOM樹(shù)中的所有節點(diǎn)都是從Node對象繼承而來(lái)的。Node對象定義了一些最基本的屬性和方法,利用這些方法可以實(shí)現對樹(shù)的遍歷,同時(shí),根據屬性還可以得知節點(diǎn)的名稱(chēng)、取值并判斷其類(lèi)型。
利用DOM,開(kāi)發(fā)人員可以動(dòng)態(tài)地創(chuàng )建XML、遍歷文檔、增加/刪除/修改文檔內容。DOM提供的API與編程語(yǔ)言無(wú)關(guān),所以對一些DOM標準中沒(méi)有明確定義的接口,不同解析器的實(shí)現方法也可能有所差別。為方便描述,本文的舉例均采用MSXML DOM方案并用VB Script編寫(xiě)代碼。
2、DOM樹(shù)的結構
Document對象建立之后,就可以與XML文檔或數據島聯(lián)系在一起。數據島的加載方法是將數據島ID賦給Document對象:
<XML ID=“dsoDetails” src="/blog/“Books.xml"”></XML>
Set doc = dsoDetails.XMLDocument
加載文檔大體上分為三步:
1.使用CreateObject方法創(chuàng )建分析器實(shí)例;
2.設置async屬性為False,禁止異步加載,這樣當文檔加載完畢,控制權才會(huì )返回給調用進(jìn)程,如果想獲取文檔加載狀態(tài),可以讀取readyState屬性值;
3.使用load方法加載指定文檔。
Set doc = CreateObject(“Microsoft.XMLDOM”)
doc.async = False
doc.load “Books.xml”
XML DOM還提供了一種loadXML的方法可以把XML字符串加載到DOM樹(shù)中,使用時(shí)只要把XML字符串直接作為該方法的參數即可。
3、DOM樹(shù)的訪(fǎng)問(wèn)
在文檔加載完畢之后就可以使用documentElement屬性訪(fǎng)問(wèn)根元素:
Set rootNode = doc.documentElement
一旦建立了對DOM樹(shù)中某個(gè)節點(diǎn)(例如根節點(diǎn))的引用,就可以根據節點(diǎn)間的等級關(guān)系調用適當的方法進(jìn)行遍歷。
下面以books.xml為例說(shuō)明各種方法的使用:
<xml id=“dsoBooks”>
<?xml version=“1.0”?><booklist><book>
<title>The Gourmet Microwave</title>
<price>9.95</price>
<author>Charlotte M. Cooper</author>
<author>Shelley B. Burke</author>
<author>Regina P. Murphy</author>
</book><book>
<title>Sushi, Anyone?</title>
<price>14.99</price></book><book>
<title>Straight Talk About Computers</title> <price>19.99</price>
<author>Lars Peterson</author>
</book></booklist></xml>
建立對第二個(gè)<book>元素的引用:
Set theNode =dsoBooks.XMLDocument.documentElement.childNodes(1)
● 根節點(diǎn):theNode.ownerDocument返回Document節點(diǎn),指向XML文檔本身;
● 兄弟節點(diǎn):theNode.previousSibling返回第1個(gè)<book>元素,theNode.nextSibling返回第3個(gè)<book>元素;
● 父節點(diǎn):theNode.parentNode返回<booklist>元素;
● 子節點(diǎn):theNode.firstChild返回<title>元素,theNode.lastChild返回<price>元素,theNode.childNodes返回子節點(diǎn)集合,包括Sushi下面的所有元素。節點(diǎn)記數從0開(kāi)始,即theNode.childNodes(0)的結果與theNode.firstChild的結果是一樣的。
獲得節點(diǎn)的引用后,就可以讀取節點(diǎn)的相關(guān)信息:
● 節點(diǎn)類(lèi)型:theNode.nodeType,本例為1,Document對象類(lèi)型為9,元素類(lèi)型為1,屬性類(lèi)型為2;
● 節點(diǎn)名稱(chēng):theNode.nodeName,本例為book;
● 節點(diǎn)值:theNode.nodevalue,本例為null,對于A(yíng)ttr節點(diǎn),返回的是屬性值,而對于Element節點(diǎn),返回的是null。
在MSXML中,對Node對象還提供了一些額外的方法和屬性:
● nodeTypeString:用字符串的方式顯示節點(diǎn)類(lèi)型,如theNode.nodeTypeString的結果是“element”;
● text: 顯示當前節點(diǎn)及其所有子節點(diǎn)的文本內容;
● xml:獲取XML文檔數據,通常是從根元素開(kāi)始的所有內容。
4、XML格式的動(dòng)態(tài)轉換
通過(guò)學(xué)習XSL,我們已經(jīng)能夠使用樣式單對XML文檔進(jìn)行轉換。但這種過(guò)程是靜態(tài)的,即在編寫(xiě)代碼時(shí),已經(jīng)指定了作用在XML上的XSL文件,在程序運行過(guò)程中不能再做改變。而利用DOM,我們能夠實(shí)現XML格式的動(dòng)態(tài)轉換,即在程序運行時(shí),將XSL載入并對XML文檔進(jìn)行轉換。
把XSL載入DOM對象的步驟基本上與XML文檔的載入過(guò)程是一樣的(XSL本身就是XML文檔):
Set stylesheet = CreateObject(“Microsoft.XMLDOM”)
stylesheet.async = False
stylesheet.load “TransformDetails.xsl”
DOM提供了兩個(gè)函數進(jìn)行這種轉換,作用對象可以是樹(shù)中任何節點(diǎn)。這樣就可以實(shí)現對DOM樹(shù)的任意的部分進(jìn)行格式轉換。
● transformNodeToObject方法:該方法需要兩個(gè)參數,第一個(gè)參數指向XSL文件,第二個(gè)參數存放轉換后的XML數據的節點(diǎn)。例如:
Set targetNode = CreateObject(“Microsoft.XMLDOM”)
srcNode.transformNodeToObject stylesheet, targetNode
● transformNode方法:該方法只需要一個(gè)參數指明XSL文件。如下例是將源節點(diǎn)轉換為一個(gè)字符串變量str:
str = srcNode.transformNode(stylesheet)
上面介紹的方法在服務(wù)器上一樣可以實(shí)現,而且更具實(shí)際意義:服務(wù)器可以根據客戶(hù)端的不同身份,使用不同的樣式單進(jìn)行轉換,同一份XML文檔呈現在不同用戶(hù)面前的形式是不一樣的!
1、DOM解析時(shí)的錯誤
DOM在解析XML文檔的時(shí)候可能會(huì )產(chǎn)生各式各樣的錯誤,可以根據ParseError對象中的屬性得知出錯的可能原因及相關(guān)信息。
常用的屬性及其含義如下表所示:
屬性 說(shuō)明
errorCode 錯誤代碼
filepos 錯誤在文檔中的絕對字符位置
line 錯誤所在行的行號
linepos 錯誤所在行的字符位置
reason 錯誤產(chǎn)生原因
srcText 錯誤所在行的源代碼
url 最近一份含有解析錯誤的XML文檔的URL地址
2、訪(fǎng)問(wèn)DOM樹(shù)中的元素與屬性
DOM還提供了許多查找節點(diǎn)的方法。其中基于搜索機制的方法有:
● 根據標簽名稱(chēng)搜索元素;
● 使用XSL模式搜索節點(diǎn);
● 利用集合索引搜索節點(diǎn)。
以books.xml為例,Document對象中的getElementsByTagName方法就是根據參數中的標簽名稱(chēng)在全文范圍內查找元素的,返回值是一個(gè)NodeList對象:
Set doc = dsoDetails.XMLDocument
Set authors = doc.getElementsByTagName(“author”)
上述查詢(xún)結果中包含了文檔中出現的全部4位作者。如果調用的是Element對象中的getElementsByTagName方法,除了搜索范圍縮小為該元素的所有后繼節點(diǎn)之外,其他的情況都是一樣的。
所有類(lèi)型的節點(diǎn)都帶有selectNodes方法,該方法的唯一參數是XSL的模式規則,返回值是匹配該規則的結果集合。調用這個(gè)方法可以利用XSL的模式匹配策略查找節點(diǎn)。例如:
Set rootNode = doc.documentElement
Set cheapbooks = rootNode.selectNodes(“//book[price < 10]”)
這個(gè)例子返回所有售價(jià)低于10元的<book>元素。另外,節點(diǎn)中的selectSingleNodes方法的用法與selectNodes是一樣的,只是返回結果為查找到的滿(mǎn)足條件的第一個(gè)節點(diǎn)而已。
對于元素節點(diǎn),獲取元素標簽名稱(chēng)的方法有兩種:anyElement.nodeName和anyElement.tagName。前者是Node對象的屬性,后者是Element對象的屬性。
如果想獲取元素中的文本內容,如: <price>9.95
</price>時(shí),訪(fǎng)問(wèn)Element對象中的nodevalue屬性是錯誤的,這時(shí)返回結果是null,而不是預期的9.95。含有文本內容的元素都包含一個(gè)Text類(lèi)型的子節點(diǎn),所以只有通過(guò)Text對象中的nodevalue屬性才能真正訪(fǎng)問(wèn)到文本內容。
添加元素的步驟如下:
● 創(chuàng )建一個(gè)Text節點(diǎn)并賦值;
● 創(chuàng )建Element節點(diǎn);
● 把Text節點(diǎn)掛在Element節點(diǎn)下,作為它的子節點(diǎn);
● 把Element節點(diǎn)插入到XML文檔的合適位置上。
對于元素節點(diǎn)的刪除和替換操作,首先都要對操作對象進(jìn)行定位,然后相應地執行對象節點(diǎn)所屬父節點(diǎn)的removeChild方法和replaceChild方法即可。
Attr節點(diǎn)的各種操作在原理上與Element節點(diǎn)相同。Attr對象同樣繼承了Node對象中的各種方法和屬性,并且MSXML中還提供了name屬性和value屬性,能夠更直接地訪(fǎng)問(wèn)到屬性信息。另外,還可以通過(guò)屬性所屬元素的相關(guān)方法訪(fǎng)問(wèn)屬性,如通過(guò)getAttribute和setAttribute方法讀取屬性值或修改屬性值,或者用getAttributeNode方法直接返回Attr對象。
創(chuàng )建新屬性最直接的辦法就是使用Element對象中的setAttribute方法。也可以先用Document對象中的createAttribute方法設置屬性值,然后使用Element對象中的setAttributeNode方法把新節點(diǎn)添加到DOM樹(shù)中。同樣地,刪除屬性最直接的方法是調用Element中的removeAttribute方法。另外一種方案是先用getAttributeNode方法對操作對象進(jìn)行定位,然后執行removeAttributeNode操作。
從以上的介紹中可以看出,由于節點(diǎn)間的繼承關(guān)系,以及各種類(lèi)型節點(diǎn)本身提供的豐富的接口,用戶(hù)可以很容易地找到一套適合自己需要的對象操作方案。
3、DOM的顯示功能
DOM技術(shù)還可以用來(lái)顯示XML數據。XSL樣式單面向的是XML文檔的轉換,用于顯示格式的轉換是其應用的一個(gè)方面,所以在顯示功能上還存在著(zhù)一些不足:
● 不易完成對XML數據的復雜處理,如將英文字母全部轉換為大寫(xiě),截取指定長(cháng)度的字符串,忽略一些特定的標點(diǎn)等;
● 不易對XML數據中的數值進(jìn)行計算;
● 一個(gè)XSL通常是靜態(tài)地作用到一個(gè)XML文檔上,無(wú)法將多個(gè)XML文檔中的數據用一個(gè)XSL合并轉換為一個(gè)輸出結果。
使用DOM就能夠很好地解決上述問(wèn)題,而且編寫(xiě)的腳本在服務(wù)器端和客戶(hù)端都能夠執行。在客戶(hù)端可以根據情況創(chuàng )建友好的用戶(hù)界面與用戶(hù)進(jìn)行交互,在服務(wù)器端使用腳本利用DOM可以按客戶(hù)端的需求將數據進(jìn)行組織并且發(fā)送。
下面的這個(gè)例子就是把XML文檔轉換成HTML中的表格:
Dim outstr
outstr = “<TABLE BORDER=1>”
Set listOrderitem = doc.selectNodes(“//orderitem”)
For Each node In listOrderitem
outstr = outstr & “<TR>” & _
“<TD>” & node.getAttribute(“title”) & _
“</TD>” & _
“<TD>” & node.getAttribute(“isbn”) & _
“</TD></TR>”
Next
outstr = outstr & “</TABLE>”
下面的這個(gè)例子則是對XML數據進(jìn)行數值運算,計算書(shū)本的平均售價(jià):
totalPrice = 0
Set listPrice = doc.selectNodes(“//price”)
For Each node In listPrice
totalPrice = totalPrice + node.firstChild.nodevalue
Next
avgPrice = totalPrice / listPrice.length
最后的這個(gè)例子顯示了DOM 把多個(gè)XML文檔合并轉換顯示的強大功能:
<XML ID=“dsoOrders” src="/blog/“Orders.xml"”></XML>
<XML ID=“dsoCustomers” src="/blog/“Customers.xml"”></XML>
Set docOrders = dsoOrders.XMLDocument
Set docCustomers = dsoCustomers.XMLDocument
outstr = “<P>Number of order items: ” & _
docOrders.selectNodes(“//orderitem”).length & _
“<P>Number of customers: ” & _
docCustomers.selectNodes(“//customer”).length
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請
點(diǎn)擊舉報。