《Javascript 高階程式設計(第三版)》筆記0x1F JavaScript 與 XML
目錄
瀏覽器對 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.");
}
}