1. 程式人生 > >二十六、XML

二十六、XML

aso 模式 測試 ext dom2 val body sof eof

二十六、XML

隨著互聯網的發展,Web應用程序的豐富,開發人員越來越希望能夠使用客戶端來操作XML技術。而XML技術一度成為存儲和傳輸結構化數據的標準。所以,本章就詳細探討一下JavaScript中使用XML的技術。

對於什麽是XML,幹什麽用的,這裏就不在贅述了,在以往的XHTML或PHP課程都有涉及到,可以理解成一個微型的結構化的數據庫,保存一些小型數據用的。

1.IE中的XML

在統一的正式規範出來以前,瀏覽器對於XML的解決方案各不相同。DOM2級提出了動態創建XML DOM規範,DOM3進一步增強了XML DOM。所以,在不同的瀏覽器實現XML的處理是一件比較麻煩的事情。

1.創建XMLDOM對象

IE瀏覽器是第一個原生支持XML的瀏覽器,而它是通過ActiveX對象實現的。這個對象,只有IE有,一般是IE9之前采用。微軟當年為了開發人員方便的處理XML,創建了MSXML庫,但卻沒有讓Web開發人員通過瀏覽器訪問相同的對象。

var xmlDom = new ActiveXObject(‘MSXML2.DOMDocument‘);

ActiveXObject類型

XML版本字符串

說明

Microsoft.XmlDom

最初隨同IE發布,不建議使用

MSXML2.DOMDocument

腳本處理而更新的版本,僅在特殊情況作為備份用

MSXML2.DOMDocument.3.0

在JavaScript中使用,這是最低的建議版本

MSXML2.DOMDocument.4.0

腳本處理時並不可靠,使用這個版本導致安全警告

MSXML2.DOMDocument.5.0

腳本處理時並不可靠,使用這個版本導致安全警告

MSXML2.DOMDocument.6.0

腳本能夠可靠處理的最新版本

PS:在這六個版本中微軟只推薦三種:

1.MSXML2.DOMDocument.6.0 最可靠最新的版本

2.MSXML2.DOMDocument.3.0 兼容性較好的版本

3.MSXML2.DOMDocument 僅針對IE5.5之前的版本

PS:這三個版本在不同的windows平臺和瀏覽器下會有不同的支持,那麽為了實現兼容,我們應該考慮這樣操作:從6.0->3.0->備用版本這條路線進行實現。

function createXMLDOM() {

var version = [

‘MSXML2.DOMDocument.6.0‘,

‘MSXML2.DOMDocument.3.0‘,

‘MSXML2.DOMDocument‘

];

for (var i = 0; i < version.length; i ++) {

try {

var xmlDom = new ActiveXObject(version[i]);

return xmlDom;

} catch (e) {

//跳過

}

}

throw new Error(‘您的系統或瀏覽器不支持MSXML!‘); //循環後拋出錯誤

}

三.載入XML

如果已經獲取了XMLDOM對象,那麽可以使用loadXML()和load()這兩個方法可以分別載入XML字符串或XML文件。

xmlDom.loadXML(‘<root version="1.0"><user>Lee</user></root>‘);

alert(xmlDom.xml);

PS:loadXML參數直接就是XML字符串,如果想效果更好,可以添加換行符\n。.xml屬性可以序列化XML,獲取整個XML字符串。

xmlDom.load(‘test.xml‘); //載入一個XML文件

alert(xmlDom.xml);

當你已經可以加載了XML,那麽你就可以用之前學習的DOM來獲取XML數據,比如標簽內的某個文本。

var user = xmlDom.getElementsByTagName(‘user‘)[0]; //獲取<user>節點

alert(user.tagName); //獲取<user>元素標簽

alert(user.firstChild.nodeValue); //獲取<user>裏的值Lee

DOM不單單可以獲取XML節點,也可以創建。

var email= xmlDom.createElement(‘email‘);

xmlDom.documentElement.appendChild(email);

四.同步及異步

load()方法是用於服務器端載入XML的,並且限制在同一臺服務器上的XML文件。那麽在載入的時候有兩種模式:同步和異步。

所謂同步:就是在加載XML完成之前,代碼不會繼續執行,直到完全加載了XML再返回。好處就是簡單方便、壞處就是如果加載的數據停止響應或延遲太久,瀏覽器會一直堵塞從而造成假死狀態。

xmlDom.async = false; //設置同步,false,可以用PHP測試假死

所謂異步:就是在加載XML時,JavaScript會把任務丟給瀏覽器內部後臺去處理,不會造成堵塞,但要配合readystatechange事件使用,所以,通常我們都使用異步方式。

xmlDom.async = true; //設置異步,默認

通過異步加載,我們發現獲取不到XML的信息。原因是,它並沒有完全加載XML就返回了,也就是說,在瀏覽器內部加載一點,返回一點,加載一點,返回一點。這個時候,我們需要判斷是否完全加載,並且可以使用了,再進行獲取輸出。

XML DOM中readystatechange事件

就緒狀態

說明

1

DOM正在加載

2

DOM已經加載完數據

3

DOM已經可以使用,但某些部分還無法訪問

4

DOM已經完全可以

PS:readyState可以獲取就緒狀態值

var xmlDom = createXMLDOM();

xmlDom.async = true; //異步,可以不寫

xmlDom.onreadystatechange = function () {

if (xmlDom.readyState == 4) { //完全加載了,再去獲取XML

alert(xmlDom.xml);

}

}

xmlDom.load(‘test.xml‘); //放在後面重點體現異步的作用

PS:可以通過readyState來了解事件的執行次數,將load()方法放到最後不會因為代碼的順序而導致沒有加載。並且load()方法必須放在onreadystatechange之後,才能保證就緒狀態變化時調用該事件處理程序,因為要先觸發。用PHP來測試,在瀏覽器內部執行時,是否能操作,是否會假死。

PS:不能夠使用this,不能夠用IE的事件處理函數,原因是ActiveX控件為了預防安全性問題。

PS:雖然可以通過XML DOM文檔加載XML文件,但公認的還是XMLHttpRequest對象比較好。這方面內容,我們在Ajax章節詳細了解。

五.解析錯誤

在加載XML時,無論使用loadXML()或load()方法,都有可能遇到XML格式不正確的情況。為了解決這個問題,微軟的XML DOM提供了parseError屬性。

parseError屬性對象

屬性

說明

errorCode

發生的錯誤類型的數字代號

filepos

發生錯誤文件中的位置

line

錯誤行號

linepos

遇到錯誤行號那一行上的字符的位置

reason

錯誤的解釋信息

if (xmlDom.parseError == 0) {

alert(xmlDom.xml);

} else {

throw new Error(‘錯誤行號:‘ + xmlDom.parseError.line +

‘\n錯誤代號:‘ + xmlDom.parseError.errorCode +

‘\n錯誤解釋:‘ + xmlDom.parseError.reason);

}

2.DOM2中的XML

IE可以實現了對XML字符串或XML文件的讀取,其他瀏覽器也各自實現了對XML處理功能。DOM2級在document.implementaion中引入了createDocument()方法。IE9、Firefox、Opera、Chrome和Safari都支持這個方法。

1.創建XMLDOM對象

var xmlDom = document.implementation.createDocument(‘‘,‘root‘,null); //創建xmlDom

var user = xmlDom.createElement(‘user‘); //創建user元素

xmlDom.getElementsByTagName(‘root‘)[0].appendChild(user); //添加到root下

var value = xmlDom.createTextNode(‘Lee‘); //創建文本

xmlDom.getElementsByTagName(‘user‘)[0].appendChild(value); //添加到user下

alert(xmlDom.getElementsByTagName(‘root‘)[0].tagName);

alert(xmlDom.getElementsByTagName(‘user‘)[0].tagName);

alert(xmlDom.getElementsByTagName(‘user‘)[0].firstChild.nodeValue);

PS:由於DOM2中不支持loadXML()方法,所以,無法簡易的直接創建XML字符串。所以,只能采用以上的做法。

PS:createDocument()方法需要傳遞三個參數,命名空間,根標簽名和文檔聲明,由於JavaScript管理命名空間比較困難,所以留空即可。文檔聲明一般根本用不到,直接null即可。命名空間和文檔聲明留空,表示創建XMLDOM對象不需要命名空間和文檔聲明。

PS:命名空間的用途是防止太多的重名而進行的分類,文檔類型表明此文檔符合哪種規範,而這裏創建XMLDOM不需要使用這兩個參數,所以留空即可。

1.載入XML

DOM2只支持load()方法,載入一個同一臺服務器的外部XML文件。當然,DOM2也有async屬性,來表面同步或異步,默認異步。

//同步情況下

var xmlDom = document.implementation.createDocument(‘‘,‘root‘,null);

xmlDom.async = false;

xmlDom.load(‘test.xml‘);

alert(xmlDom.getElementsByTagName(‘user‘)[0].tagName);

//異步情況下

var xmlDom = document.implementation.createDocument(‘‘,‘root‘,null);

xmlDom.async = true;

addEvent(xmlDom, ‘load‘, function () { //異步直接用onload即可

alert(this.getElementsByTagName(‘user‘)[0].tagName);

});

xmlDom.load(‘test.xml‘);

PS:不管在同步或異步來獲取load()方法只有Mozilla的Firefox才能支持,只不過新版的Opera也是支持的,其他瀏覽器則不支持。

2.DOMParser類型

由於DOM2沒有loadXML()方法直接解析XML字符串,所以提供了DOMParser類型來創建XML DOM對象。IE9、Safari、Chrome和Opera都支持這個類型。

var xmlParser = new DOMParser(); //創建DOMParser對象

var xmlStr = ‘<user>Lee</user></root>‘; //XML字符串

var xmlDom = xmlParser.parseFromString(xmlStr, ‘text/xml‘); //創建XML DOM對象

alert(xmlDom.getElementsByTagName(‘user‘)[0].tagName); //獲取user元素標簽名

PS:XML DOM對象是通過DOMParser對象中的parseFromString方法來創建的,兩個參數:XML字符串和內容類型text/xml。

3.XMLSerializer類型

由於DOM2沒有序列化XML的屬性,所以提供了XMLSerializer類型來幫助序列化XML字符串。IE9、Safari、Chrome和Opera都支持這個類型。

var serializer = new XMLSerializer(); //創建XMLSerializer對象

var xml = serializer.serializeToString(xmlDom); //序列化XML

alert(xml);

5.解析錯誤

在DOM2級處理XML發生錯誤時,並沒有提供特有的對象來捕獲錯誤,而是直接生成另一個錯誤的XML文檔,通過這個文檔可以獲取錯誤信息。

var errors = xmlDom.getElementsByTagName(‘parsererror‘);

if (errors.length > 0) {

throw new Error(‘XML格式有誤:‘ + errors[0].textContent);

}

PS:errors[0].firstChild.nodeValue也可以使用errors[0].textContent來代替。

3.跨瀏覽器處理XML

如果要實現跨瀏覽器就要思考幾個個問題:1.load()只有IE、Firefox、Opera支持,所以無法跨瀏覽器;2.獲取XML DOM對象順序問題,先判斷先進的DOM2的,然後再去判斷落後的IE;3.針對不同的IE和DOM2級要使用不同的序列化。4.針對不同的報錯進行不同的報錯機制。

//首先,我們需要跨瀏覽器獲取XML DOM

function getXMLDOM(xmlStr) {

var xmlDom = null;

if (typeof window.DOMParser != ‘undefined‘) { //W3C

xmlDom = (new DOMParser()).parseFromString(xmlStr, ‘text/xml‘);

var errors = xmlDom.getElementsByTagName(‘parsererror‘);

if (errors.length > 0) {

throw new Error(‘XML解析錯誤:‘ + errors[0].firstChild.nodeValue);

}

} else if (typeof window.ActiveXObject != ‘undefined‘) { //IE

var version = [

‘MSXML2.DOMDocument.6.0‘,

‘MSXML2.DOMDocument.3.0‘,

‘MSXML2.DOMDocument‘

];

for (var i = 0; i < version.length; i ++) {

try {

xmlDom = new ActiveXObject(version[i]);

} catch (e) {

//跳過

}

}

xmlDom.loadXML(xmlStr);

if (xmlDom.parseError != 0) {

throw new Error(‘XML解析錯誤:‘ + xmlDom.parseError.reason);

}

} else {

throw new Error(‘您所使用的系統或瀏覽器不支持XML DOM!‘);

}

return xmlDom;

}

//其次,我們還必須跨瀏覽器序列化XML

function serializeXML(xmlDom) {

var xml = ‘‘;

if (typeof XMLSerializer != ‘undefined‘) {

xml = (new XMLSerializer()).serializeToString(xmlDom);

} else if (typeof xmlDom.xml != ‘undefined‘) {

xml = xmlDom.xml;

} else {

throw new Error(‘無法解析XML!‘);

}

return xml;

}

PS:由於兼容性序列化過程有一定的差異,可能返回的結果字符串可能會有一些不同。至於load()加載XML文件則因為只有部分瀏覽器支持而無法跨瀏覽器。

二十六、XML