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