1. 程式人生 > >《Javascript 高階程式設計(第三版)》筆記0x1F JavaScript 與 XML

《Javascript 高階程式設計(第三版)》筆記0x1F JavaScript 與 XML

目錄

瀏覽器對 XML DOM 的支援

    DOM2 級核心

    DOMParser型別

    XMLSerializer型別

    IE8 及之前版本中的XML

        序列化 XML

        載入 XML 檔案

    跨瀏覽器處理XML

瀏覽器對 XPath 的支援

    DOM3 級XPath

        單節點結果

        簡單型別結果

        預設型別結果

        名稱空間支援

    IE中的XPath

        IE 對名稱空間的支援

    跨瀏覽器使用XPath

 瀏覽器對 XSLT 的支援

    IE中的XSLT

        簡單的 XSLT 轉換

        複雜的 XSLT 轉換

    XSLTProcessor型別

    跨瀏覽器使用XSLT


瀏覽器對 XML DOM 的支援

    DOM2 級核心

//檢測瀏覽器是否支援 DOM2 級 XML
var hasXmlDom = document.implementation.hasFeature("XML", "2.0");
//建立一個新的、文件元素為<root>的 XML 文件
var xmldom = document.implementation.createDocument("", "root", null);
alert(xmldom.documentElement.tagName); //"root"
var child = xmldom.createElement("child");
xmldom.documentElement.appendChild(child);

    DOMParser型別

    將XML解析為DOM文件,在解析XML之前,先必須建立一個DOMParser的例項,再呼叫parseFromString()方法。這個方法接受兩個引數:要解析的XML字串和內容型別(內容型別始終都應該是"text/xml")。返回的值是一個Document的例項。

var parser = new DOMParser();
var xmldom = parser.parseFromString("<root><child/></root>", "text/xml");
alert(xmldom.documentElement.tagName); //"root"
alert(xmldom.documentElement.firstChild.tagName); //"child"
var anotherChild = xmldom.createElement("child");
xmldom.documentElement.appendChild(anotherChild);
var children = xmldom.getElementsByTagName("child");
alert(children.length); //2

    XMLSerializer型別

    將 DOM 文件序列化為 XML 字串。要序列化 DOM 文件,首先必須建立 XMLSerializer 的例項,然後將文件傳入其 serializeToString ()方法,該方法返回的字串並不適合列印,因此看起來會顯得亂糟糟的。

var serializer = new XMLSerializer();
var xml = serializer.serializeToString(xmldom);
alert(xml)

    IE8 及之前版本中的XML

    IE 是第一個原生支援 XML 的瀏覽器,而這一支援是通過 ActiveX 物件實現的。要建立一個 XML 文件的例項,也要使用ActiveXObject 建構函式併為其傳入一個表示XML 文件版本的字串。

//嘗試建立每個版本的例項並觀察是否有錯誤發生,可以確定哪個版本可用
function createDocument(){
	if (typeof arguments.callee.activeXString != "string"){
		var versions = ["MSXML2.DOMDocument.6.0", "MSXML2.DOMDocument.3.0","MSXML2.DOMDocument"],
			i, len;
		for (i=0,len=versions.length; i < len; i++){
			try {
				new ActiveXObject(versions[i]);
				arguments.callee.activeXString = versions[i];
				break;
			} catch (ex){
				//跳過
			}
		}
	}
	return new ActiveXObject(arguments.callee.activeXString);
}

    要解析 XML 字串,首先必須建立一個 DOM 文件,然後呼叫 loadXML()方法。新建立的 XML文件完全是一個空文件,因而不能對其執行任何操作。為 loadXML()方法傳入的 XML 字串經解析之後會被填充到 DOM 文件中。

var xmldom = createDocument();
xmldom.loadXML("<root><child/></root>");
alert(xmldom.documentElement.tagName); //"root"
alert(xmldom.documentElement.firstChild.tagName); //"child"
var anotherChild = xmldom.createElement("child");
xmldom.documentElement.appendChild(anotherChild);
var children = xmldom.getElementsByTagName("child");
alert(children.length); //2

        序列化 XML

    IE 將序列化 XML 的能力內建在了 DOM 文件中。每個 DOM 節點都有一個 xml 屬性,其中儲存著表示該節點的 XML 字串。

        載入 XML 檔案

    與 DOM3 級中的功能類似,要載入的 XML文件必須與頁面中執行的 JavaScript 程式碼來自同一臺伺服器。同樣與 DOM3 級規範類似,載入文件的方式也可以分為同步和非同步兩種。要指定載入文件的方式,可以設定 async 屬性, true 表示非同步, false表示同步(預設值為 true)。 

var xmldom = createDocument();
xmldom.async = false;
xmldom.load("example.xml");
if (xmldom.parseError != 0){
	//處理錯誤
} else {
	alert(xmldom.documentElement.tagName); //"root"
	alert(xmldom.documentElement.firstChild.tagName); //"child"
	var anotherChild = xmldom.createElement("child");
	xmldom.documentElement.appendChild(anotherChild);
	var children = xmldom.getElementsByTagName("child");
	alert(children.length); //2
	alert(xmldom.xml);
}

    由於是以同步方式處理 XML 檔案,因此在解析完成之前,程式碼不會繼續執行,這樣的程式設計工作要簡單一點。雖然同步方式比較方便,但如果下載時間太長,會導致程式反應很慢。因此,在載入 XML文件時,通常都使用非同步方式。

var xmldom = createDocument();
xmldom.async = true;
xmldom.onreadystatechange = function(){
	if (xmldom.readyState == 4){
		if (xmldom.parseError != 0){
			alert("An error occurred:\nError Code: "
			+ xmldom.parseError.errorCode + "\n"
			+ "Line: " + xmldom.parseError.line + "\n"
			+ "Line Pos: " + xmldom.parseError.linepos + "\n"
			+ "Reason: " + xmldom.parseError.reason);
		} else {
			alert(xmldom.documentElement.tagName); //"root"
			alert(xmldom.documentElement.firstChild.tagName); //"child"
			var anotherChild = xmldom.createElement("child");
			xmldom.documentElement.appendChild(anotherChild);
			var children = xmldom.getElementsByTagName("child");
			alert(children.length); //2
			alert(xmldom.xml);
		}
	}
};
xmldom.load("example.xml");

    跨瀏覽器處理XML

//解析 XML 
function parseXml(xml){
	var xmldom = null;
	if (typeof DOMParser != "undefined"){
		xmldom = (new DOMParser()).parseFromString(xml, "text/xml");
		var errors = xmldom.getElementsByTagName("parsererror");
		if (errors.length){
			throw new Error("XML parsing error:" + errors[0].textContent);
		}
	} else if (typeof ActiveXObject != "undefined"){
		xmldom = createDocument();
		xmldom.loadXML(xml);
		if (xmldom.parseError != 0){
			throw new Error("XML parsing error: " + xmldom.parseError.reason);
		}
	} else {
		throw new Error("No XML parser available.");
	}
	return xmldom;
}

//序列化 XML
function serializeXml(xmldom){
	if (typeof XMLSerializer != "undefined"){
		return (new XMLSerializer()).serializeToString(xmldom);
	} else if (typeof xmldom.xml != "undefined"){
		return xmldom.xml;
	} else {
		throw new Error("Could not serialize XML DOM.");
	}
}

瀏覽器對 XPath 的支援

    DOM3 級XPath

        單節點結果

//指定常量XPathResult.FIRST_ORDERED_NODE_TYPE會返回第一個匹配的節點,可以通過結果的 singleNodeValue屬性來訪問該節點。
var result = xmldom.evaluate("employee/name", xmldom.documentElement, null,
XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (result !== null) {
	alert(result.singleNodeValue.tagName);
}
DomXPathExample03.ht

        簡單型別結果

//如果有節點匹配"employee/name",則 booleanValue 屬性的值就是 true
var result = xmldom.evaluate("employee/name", xmldom.documentElement, null,XPathResult.BOOLEAN_TYPE, null);
alert(result.booleanValue);

//計算與給定模式匹配的所有節點數量的 count()
var result = xmldom.evaluate("count(employee/name)", xmldom.documentElement,null, XPathResult.NUMBER_TYPE, null);
alert(result.numberValue);

//對於字串型別,evaluate()方法會查詢與 XPath 表示式匹配的第一個節點,然後返回其第一個子節點的值
var result = xmldom.evaluate("employee/name", xmldom.documentElement, null,XPathResult.STRING_TYPE, null);
alert(result.stringValue);

        預設型別結果

var result = xmldom.evaluate("employee/name", xmldom.documentElement, null,XPathResult.ANY_TYPE, null);
if (result !== null) {
	switch(result.resultType) {
		case XPathResult.STRING_TYPE:
			//處理字串型別
			break;
		case XPathResult.NUMBER_TYPE:
			//處理數值型別
			break;
		case XPathResult.BOOLEAN_TYPE:
			//處理布林值型別
			break;
		case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
			//處理次序不一致的節點迭代器型別
			break;
		default:
			//處理其他可能的結果型別
	}
}

        名稱空間支援

    對於利用了名稱空間的 XML 文件, XPathEvaluator 必須知道名稱空間資訊,然後才能正確地進行求值。

<?xml version="1.0" ?>
<wrox:books xmlns:wrox="http://www.wrox.com/">
	<wrox:book>
		<wrox:title>Professional JavaScript for Web Developers</wrox:title>
		<wrox:author>Nicholas C. Zakas</wrox:author>
	</wrox:book>
	<wrox:book>
		<wrox:title>Professional Ajax</wrox:title>
		<wrox:author>Nicholas C. Zakas</wrox:author>
		<wrox:author>Jeremy McPeak</wrox:author>
		<wrox:author>Joe Fawcett</wrox:author>
	</wrox:book>
</wrox:books>
//通過 createNSResolver()來建立 XPathNSResolver 物件。
var nsresolver = xmldom.createNSResolver(xmldom.documentElement);
var result = xmldom.evaluate("wrox:book/wrox:author",
			xmldom.documentElement, nsresolver,
			XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
alert(result.snapshotLength);

//定義一個函式,讓它接收一個名稱空間字首,返回關聯的 URI
var nsresolver = function(prefix){
	switch(prefix){
		case "wrox": return "http://www.wrox.com/";
		//其他字首
	}
};
var result = xmldom.evaluate("count(wrox:book/wrox:author)",
			xmldom.documentElement, nsresolver, XPathResult.NUMBER_TYPE, null);
alert(result.numberValue);

    IE中的XPath

    在 IE9 及之前的版本中使用 XPath,必須使用基於 ActiveX 的實現。這個介面在每個節點上額外定義了兩個的方法: selectSingleNode()和 selectNodes()。其中,selectSingleNode()方法接受一個 XPath 模式,在找到匹配節點時返回第一個匹配的節點,如果沒有找到匹配的節點就返回 null。

var element = xmldom.documentElement.selectSingleNode("employee/name");
if (element !== null){
  alert(element.xml);
}

var elements = xmldom.documentElement.selectNodes("employee/name");
alert(elements.length);

        IE 對名稱空間的支援

//要在 IE 中處理包含名稱空間的 XPath 表示式,你必須知道自己使用的名稱空間,並按照下列格式建立一個字串
//"xmlns:prefix1='uri1' xmlns:prefix2='uri2' xmlns:prefix3='uri3'"
xmldom.setProperty("SelectionNamespaces", "xmlns:wrox=’http://www.wrox.com/’");
var result = xmldom.documentElement.selectNodes("wrox:book/wrox:author");
alert(result.length);

    跨瀏覽器使用XPath

   selectSingleNode(),它接收三個引數:上下文節點、 XPath表示式和可選的名稱空間物件

function selectSingleNode(context, expression, namespaces){
	var doc = (context.nodeType != 9 ? context.ownerDocument : context);
	if (typeof doc.evaluate != "undefined"){
		var nsresolver = null;
		if (namespaces instanceof Object){
			nsresolver = function(prefix){
				return namespaces[prefix];
			};
		}
		var result = doc.evaluate(expression, context, nsresolver,
			XPathResult.FIRST_ORDERED_NODE_TYPE, null);
		return (result !== null ? result.singleNodeValue : null);
	} else if (typeof context.selectSingleNode != "undefined"){
		//建立名稱空間字串
		if (namespaces instanceof Object){
			var ns = "";
			for (var prefix in namespaces){
				if (namespaces.hasOwnProperty(prefix)){
					ns += "xmlns:" + prefix + "='" + namespaces[prefix] + "' ";
				}
			}
			doc.setProperty("SelectionNamespaces", ns);
		}
		return context.selectSingleNode(expression);
	} else {
		throw new Error("No XPath engine found.");
	}
}

var result = selectSingleNode(xmldom.documentElement, "wrox:book/wrox:author",{ wrox: "http://www.wrox.com/" });
alert(serializeXml(result));

    selectNodes()函式接收與 selectSingleNode()相同的三個引數,而且大部分邏輯都相似。

function selectNodes(context, expression, namespaces){
	var doc = (context.nodeType != 9 ? context.ownerDocument : context);
	if (typeof doc.evaluate != "undefined"){
		var nsresolver = null;
		if (namespaces instanceof Object){
			nsresolver = function(prefix){
				return namespaces[prefix];
			};
		}
		var result = doc.evaluate(expression, context, nsresolver,
		XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
		var nodes = new Array();
		if (result !== null){
			for (var i=0, len=result.snapshotLength; i < len; i++){
				nodes.push(result.snapshotItem(i));
			}
		}
		return nodes;
	} else if (typeof context.selectNodes != "undefined"){
		//建立名稱空間字串
		if (namespaces instanceof Object){
			var ns = "";
			for (var prefix in namespaces){
				if (namespaces.hasOwnProperty(prefix)){
					ns += "xmlns:" + prefix + "='" + namespaces[prefix] + "' ";
				}
			}
			doc.setProperty("SelectionNamespaces", ns);
		}
		var result = context.selectNodes(expression);
		var nodes = new Array();
		for (var i=0,len=result.length; i < len; i++){
			nodes.push(result[i]);
		}
		return nodes;
	} else {
		throw new Error("No XPath engine found.");
	}
}

var result = selectNodes(xmldom.documentElement, "wrox:book/wrox:author",{ wrox: "http://www.wrox.com/" });
alert(result.length);

 瀏覽器對 XSLT 的支援

    XSLT 是與 XML 相關的一種技術,它利用 XPath 將文件從一種表現形式轉換成另一種表現形式。與XML 和 XPath 不同, XSLT 沒有正式的 API,在正式的 DOM 規範中也沒有它的位置。結果,只能依靠瀏覽器開發商以自己的方式來實現它。 IE 是第一個支援通過 JavaScript 處理 XSLT 的瀏覽器。

    IE中的XSLT

        簡單的 XSLT 轉換

result = xmldom.documentElement.transformNode(xsltdom);
result = xmldom.documentElement.childNodes[1].transformNode(xsltdom);
result = xmldom.getElementsByTagName("name")[0].transformNode(xsltdom);
result = xmldom.documentElement.firstChild.lastChild.transformNode(xsltdom);

        複雜的 XSLT 轉換

    雖然 transformNode()方法提供了基本的 XSLT 轉換能力,但還有使用這種語言的更復雜的方式。為此,必須要使用 XSL 模板和 XSL 處理器。

    XSLTProcessor型別

    開發人員可以通過XSLTProcessor 型別使用 XSLT 轉換 XML 文件,其方式與在 IE 中使用 XSL 處理器類似。因為這個型別是率先出現的,所以 Chrome、 Safari 和 Opera 都借鑑了相同的實現,最終使 XSLTProcessor 成為了通過 JavaScript 進行 XSLT 轉換的事實標準。

    跨瀏覽器使用XSLT

function transform(context, xslt){
	if (typeof XSLTProcessor != "undefined"){
		var processor = new XSLTProcessor();
		processor.importStylesheet(xslt);
		var result = processor.transformToDocument(context);
		return (new XMLSerializer()).serializeToString(result);
	} else if (typeof context.transformNode != "undefined") {
		return context.transformNode(xslt);
	} else {
		throw new Error("No XSLT processor available.");
	}
}