二十六、XML
二十六、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