拿下AJAX三(下)——DOM与xml及xpath
Javascript中装载XML文档
装载同域的XML文件
装载一段表示XML的字符串
装载的js代码
function loadXML(flag,xmldoc){ if(window.ActiveXObject){ //IE浏览器 var activexName=["MSXML2.DOMDocument","Miscrosoft.XmlDom"]; var xmlObj; for(var i=0;i<activexName.length;i++){ try{ xmlObj=new ActiveXObject(activexName[i]); break; }catch(e){ } } if(xmlObj){ //同步方式装载XML数据 xmlObj.async=false; if(flag){ //装载XML的文件 xmlObj.load(xmldoc); }else{ //装载XML的字符串 xmlObj.loadXML(xmldoc); } //返回根节点 //return xmlObj; //返回根元素节点 return xmlObj.documentElement; }else{ alert("装载XML文档的对象创建失败"); return null; } }else if(document.implementation.createDocument){ //针对Firefox类浏览器 var xmlObj; if(flag){ //获得XML文档 xmlObj=document.implementation.createDocument("","",null); if(xmlObj){ //同步方法进行装载 xmlObj.async=false; xmlObj.load(xmldoc); return xmlObj.documentElement; }else{ alert("装载XML文档的对象创建失败"); return null; } }else{ //装载XML字符串 xmlObj=new DOMParser(); var docRoot=xmlObj.parseFromString(xmldoc,"text/xml"); return docRoot.documentElement; } } alert("装载文档的对象创建失败"); return null;}
调用装载代码:
function text(){ var domxml1=loadXML(true,"test.xml"); alert(domxml1); var domxml2=loadXML(false,"<xml>123</xml>") }
IE和FireFox类的浏览器对于这两种情况分别有各自的处理方式,因此我们需要写一个封装方法,方便以后进行XML文档的装载操作。
注意和XMLHttpRequest对象一样,跨域装载XML文档会被浏览器禁止。
DOM 操作XML和操作HTML文档相同,可以利用DOM的API进行XML数据的获取和修改
function test(){ var rootElement=loadXML(true,"testBook.xml"); var rootDocument=rootElement.parentNode; var bookElement=rootDocument.createElement("book"); var textNode=rootDocument.createTextNode("AJAX Hello"); bookElement.appendChild(textNode); rootElement.appendChild(bookElement); var bookElements=rootElement.getElementsByTagName("book"); alert(""); }
需要注意document这个特殊的对象只对应于HTML的根节点。XML的根节点需要在获得XML的DOM对象后通过一定的方式获取出来。
解决浏览器处理空白信息的差异HTML文档body的子孙节点或XML根节点的子孙节点中,如果两个元素节点之间只有空格,缩进,回车这样的不包含任何文字信息的内容,那么这些信息就都是空白信息。
IE的DOM实现在解析文档时会过滤掉空白信息,FireFox的DOM实现在解析文档时会将空白信息当成是文本节点保留。
这一差异导致使用DOM的API来遍历文档数据需要写不同的代码来适应不同的浏览器
移除所有空白信息,保证IE和FireFox下的dom树结构相同
解决浏览器处理空白信息所用的js函数
function removeBlank(doc){ if(doc.childNodes.length>1){} for(var loopIndex=0;loopIndex<doc.childNodes.length;loopIndex++){ var currentNode=doc.childNodes[loopIndex]; if(currentNode.nodeType==1){ removeBlank(currentNode); } if(((/^\s+$/.test(currentNode.nodeValue)))&&(currentNode.nodeType==3)){ doc.removeChild(doc.childNodes[loopIndex--]); } } }
调用此函数:
function test(){ var rootElement=loadXML(true,"testBook.xml"); removeBlank(rootElement); alert(serializeDOM(rootElement)); }
IE的方法:DOM对象上有一个xml的属性,可以获得对象对应的XML的字符串
FireFox的方法:新建一个XMLSerializer对象,然后使用serializeToString方法将DOM对象转换成XML字符串
function serializeDOM(xmldoc){ if(xmldoc.xml){ //IE浏览器 return xmldoc.xml; }else if(window.XMLSerializer){ var seria=new XMLSerializer(); return seria.serializeToString(xmldoc); } return null; }
GetElementById方法在解析XML时是否合适?
首先XML中每一个元素节点不一定有id属性。
其次XML中的两个元素节点可能有相同的id属性,这样getElementById这个方法就不能保证找到我们需要的唯一节点了。
因此这种方式并不合适XML的解析。
getElementById的方式一次获得的节点太多,还需要再进行遍历获取数据,比较麻烦。
XPATH即使——解决问题的良药
XPATH是什么“全称是XML PathLanguage(XML路径语言),适用于确定XML文档中某节点位置的语言。
我们可以把它当做类似于SQL一样的查询语言,通过给出XPATH路径信息(就像SQL命令一样)既可以从XML中查找出符合条件的及诶单(就像从数据库中返回需要的数据一样)。
Java中的DOM4j开源包中有selectSingleNode和selectNodes这样两个方法可以根据XPATH表达式来获取一个或多个节点。
在IE6.0及其以后版本中我们可是使用同样的方式来方位呢任意深度的XML数据,这给XML数据的解析操作带来了便利。
但是在FireFox等浏览器中,则是使用了w3c标准的XPATH处理方式,没有IE这么简单的方式。
XPATH技术——我们用一个实例来讲解
首先,写出获取路径的js函数:
function selectNodes(xmldoc,sXpath){ if(window.ActiveXObject){ //IE浏览器 return xmldoc.selectSingleNode(sXpath); }else if(window.XPathEvaluator){ //FireFox类浏览器 var xpathObj=new XPathEvaluator(); if(xpathObj){ var result=xpathObj.evaluate(sXpath,xmldoc,null,XPathResult.ORDERED_NODE_ITEARTOR_TYPE,null); var nodes=new Array(); var node; while((node = result.iterateNext())!=null) { nodes.push(node); } return nodes; }else{ return null; } }else{ return null; }}
一个重要应用:当返回结果是节点列表时的后续操作
evaluate()方法最常返回的是一个节点列表,得到节点列表后我们还要想着怎么来遍历这个列表中的所有节点。
XPathResult对象提供了4种常量:
UNORDERED_NODE_ITERATOR_TYPE:节点的无序集合,可通过重复调用iterateNext()直到返回null来访问。访问过程中要保证文档没有被修改;
ORDERED_NODE_ITERATOR_TYPE:节点的列表,按照文档中节点的属性排列。访问方法同上;
UNORDERED_NODE_SNAPSHOT_TYPE:随机访问的节点列表。snapshotLength属性指定了列表长度,并且snapshotItem()方法返回指定下标的节点。节点可能和在文档中的顺序不一样。既然这种结果是一个“快照”,因此即便文档发生变化,它也有效;
ORDERED_NODE_SNAPSHOT_TYPE:随机访问的节点列表,按照节点在文档中的顺序排列。
如果evaluate()方法中的第4个参数resultType设置的是ANY_TYPE,且返回的是节点列表,则默认的是前两个(实验结果,我也不清楚到底是第一个还是第二个),然后使用iterateNext()方法来遍历。
个人觉得第三个或第四个(快照)也不错,类似数组的操作比较简单。
在通过调用此函数来获取不同参数的值:
function testxpath(){ var rootElement=loadXML(true,"XPATH.xml"); removeBlank(rootElement); //查找所有的author元素节点 //关键就是如何表示元素在DOM 树中的路径 //绝对路径/books/book/author //相对路径 book/author //全文档搜索法//author //book/author var author1=selectNodes(rootElement,"/books/book/author"); var author2=selectNodes(rootElement,"book/author"); var author3=selectNodes(rootElement,"//author"); var author4=selectNodes(rootElement,"//book/author"); //查找属性节点 //关键是先找到包含属性节点的元素节点 var isdn1=selectNodes(rootElement,"/books/book/@isdn"); var isdn2=selectNodes(rootElement,"book/@isdn"); var isdn3=selectNodes(rootElement,"//book/@isdn"); var isdn4=selectNodes(rootElement,"//books/book/@isdn"); //查找文本节点 //关键是找到包含文本节点的那个元素节点 var text1=selectNodes(rootElement,"/books/book/name/text()"); var text2=selectNodes(rootElement,"book/name/text()"); var text3=selectNodes(rootElement,"//book/name/text()"); var text4=selectNodes(rootElement,"//name/text()"); //有条件的查找元素节点 //需要首先找到元素节点,然后再增加条件表达式 var book1=selectNodes(rootElement,"/books/book[@isdn]"); var book2=selectNodes(rootElement,"book[@isdn]"); var book3=selectNodes(rootElement,"//book[@isdn]"); //多个条件与关系的查询元素节点 //需要首先找到元素节点,然后每一个查询条件写在一个中括号中 Var bookA1=selectNodes(rootElement,"/books/book[@isdn='0002'][price>35]"); var bookA2=selectNodes(rootElement,"book[@isdn='0002'][price>35]"); var bookA3=selectNodes(rootElement,"//book[@isdn='0002'][price>35]"); //多个条件或关系的查找元素节点 //需要首先找到元素节点,然后每一个查询条件写在一个XPATH的表达式,这些表达式用|组合,表示或关系 var book01=selectNodes(rootElement,"/books/book[@isdn='0002']| /books/book[price>35]"); var book02=selectNodes(rootElement,"book[@isdn='0002']|/books/book[price>35]"); var book03=selectNodes(rootElement,"//book[@isdn='0002']|/books/book[price>35]"); alert(""); }
DOM 的语言无关性设计为其带来了非常广泛的可应用性并使其在大量的系统和平台上得以实现。这样做的代价是:使 DOM比为每个语言专门设计的 API更困难且更缺乏直观性。
DOM 奠定了一个非常有效的基础,遵循一些简单的原则就可其上构建易于使用的系统。凝结了一大群用户智慧和经验的 DOM未来版本正在设计之中,而且极有可能为这里讨论的问题提供解决方案。如 JDOM这样的项目正在修改该 API 以获得更自然 Java感觉,而且如本文中所述的技术可以帮助您使 XML的操纵更方便、更简洁并且不易出错。利用这些项目且遵循这些用法模式以允许 DOM成为基于 XML项目的出色平台。