XML可以理解成一个微型的结构化的数据库,保存一些小型数据用的。XML 技术是存储和传输结构化数据的标准
一 IE中的XML
1、创建XML DOM对象
IE是第一个支持XML文件的,它是通过ActiveX来创建的。并且一般是在IE9之前使用,微软为了开发人员能够方便的处理XML文件,提供了一个MLXML库。
|
|||||||||||||||
在上面的六个版本中,微软推荐的是MSXML2.DOMDocument.6.0 、MSXML2.DOMDocument.3.0 以及MSXML2.DOMDocument。这三个版本针对不同的浏览器版本以及操作系统其支持的特性也不是很好,最好是版本的从高到底来进行实现。
<span style="font-size:18px;"> function createXml(){ var version = [ 'MSXML2.DOMDocuemnt.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){ continue; } } //如果执行到此处,说明不能够创建,就抛出异常 throw new Error('您的操作系统或者浏览器不支持MSXML库'); } alert( createXml()) //object</span>
2、加载XML文件
当创建了xmlDom对象后,可以通过load()和loadXML()两个方法来载入XML文件,load()方法是载入一个XML文件,loadXML()方法是载入一段XML字符串。
<span style="font-size:18px;"> //加载一段XML代码 var xmlDom = createXml(); //加载XML字符串 xmlDom.loadXML("<root> <user>aaa</user> </root>"); //序列化XML,打印字符串 alert(xmlDom.xml); //默认输出为空,具体见后面同步异步小节</span>
<span style="font-size:18px;"> //加载一个外部文件<span style="white-space:pre"> </span>外部文件中的内容和上面的XML字符串差不多,只是多了两个标签 var xmlDom = createXml(); //alert(typeof xmlDom) xmlDom.load('demo.xml'); //alert( xmlDom.xml);</span>
3、获取和增加节点内容
当我们加载完成后,就可以获取里面的标签元素,以及内容,XML是标准的DOM文档,我们加载XML文件后,可以通过标准的DOM来操作它,通过getElementById、getElementsByTagNameden来获取标签,通过creatElement、appendCHild等来添加标签
<span style="font-size:18px;"> //获取XML文件中的值 var root = xmlDom.getElementsByTagName('root')[0]; alert(root.firstChild.nodeType); //1 alert(root.firstChild.tagName); //user alert(root.firstChild.innerText); //undefined //innerHTML不是标准DOM alert(root.firstChild.nodeValue); //null 它是个标签元素,所以是没有值的 alert(rot.firstChild.text); //aaa var usr = root.firstChild; alert(usr.firstChild.nodeValue); //aaa alert(user.text); //aaa</span>
<span style="font-size:18px;"> //增加节点 var xmlDom = createXml(); xmlDom.load('demo.xml'); alert(xmlDom.xml); var box = xmlDom.createElement("box"); box.text = 'abcd'; var root = xmlDom.documentElement; //获取根节点,和DOM中是一样的,DOM中返回的是<html> root.appendChild(box); //增加一个子节点 alert(xmlDom.xml); var bbb = xmlDom.createElement('bbb'); var txt = xmlDom.createTextNode('abcdefg'); bbb.appendChild(txt); bbb.setAttribute("id",'b1'); //设置属性 //bbb.name='name'; //此种方法不可以,这是HTML DOM中的方法 root.appendChild(bbb); alert(xmlDom.xml);</span>
4、同步和异步加载
load()方法是用于服务器端载入 XML 的,并且限制在同一台服务器上的 XML 文件。 那么在载入的时候有两种模式:同步和异步。
所谓同步:就是在加载 XML 完成之前,代码不会继续执行,直到完全加载了 XML 再返回。 好处就是简单方便、 坏处就是如果加载的数据停止响应或延迟太久, 浏览器会一直堵塞从而造成假死状态。
所谓异步:就是在加载 XML 时,JavaScript 会把任务丢给浏览器内部后台去处理,不会造成堵塞,但要配合 readystatechange 事件使用,所以,通常我们都使用异步方式。
同步和异步的设置:xmlDom.async = true/false;如果将值设置为true就是异步加载,默认情况下也是异步加载,设置为false就是同步加载
<span style="font-size:18px;"> var xmlDom = createXml(); xmlDom.async=true; //同步设置为false,异步为true,默认是异步的 xmlDom.load('demo.xml'); alert(xmlDom.xml); //默认是异步的, //在服务器端,上面输出的结果是空,原因:在服务器端,使用的异步加载,load()还没有加载完毕,就去打印xmlDom.xml序列化的字符串 var xmlDom = createXml(); xmlDom.async=false; //同步设置为false,异步为true,默认是异步的 xmlDom.load('demo.xml'); alert(xmlDom.xml);</span>
使用异步加载的时候,发现用xmlDom.xml根本就不会输出信息,是因为,在后台加载还没有完成,前台就已经在开始输出了,当然为空,解决办法就是通过事件readstatechange并且配合属性readState来实现。并且这个事件必须要放在加载方法之前,因为要先进行事件的注册,然后再加载的时候才会进行事件的激活,这个事件中,有四个状态,1:正在加载,2:加载完数据,但是还不能够读,3:加载完成,有的数据还无法访问,4:已经全部加载完成,这四个状态可以通过属性readyState来获取。
<span style="font-size:18px;"> var xmlDom = createXml(); xmlDom.async = true; //xmlDom.load("demo.xml");//加载语句放在前面,在readystatechange事件中就不会有输出 //在readystatechange事件中的this代表的是window对象,而不是xmlDom对象 var xmlDom = createXml(); xmlDom.async = true; xmlDom.onreadystatechange=function(){ if(xmlDom.readyState == 4){ //通过判断,就只是输出一次 alert(xmlDom.xml); alert(typeof this); //object alert(this === window); //true } } xmlDom.load("demo.xml");</span>
上面的事件中,使用的是传统的事件绑定,没有使用 IE 的事件处理函数,原因是 ActiveX 控件为了预防安全性问题。并且在事件中this对象代表的是window,而不是当前的xmlDom,见上面的代码
5、解析错误
在加载 XML 时, 无论使用 loadXML()或 load()方法, 都有可能遇到 XML 格式不正确的情况。为了解决这个问题,微软的 XML DOM 提供了 parseError 属性对象,属性如下
|
|||||||||||||||
修该同步和异步加载中的代码如下:
<span style="font-size:18px;">//在XML文件加载完成后,可能会出现解析错误,提供了一个对象parseError对象,这个对象会默认的执行属性errorCode var xmlDom = createXml(); xmlDom.async = true; xmlDom.onreadystatechange=function(){ if(xmlDom.readyState == 4){ //通过判断,就只是输出一次 //alert(xmlDom.parseError.errorCode); //正确加载输出的是0 //alert(xmlDom.parseError); //0 默认的执行的是errorCode if(xmlDom.parseError.errorCode == 0){ //代表加载没有出错 alert(xmlDom.xml); }else{ alert("错误代号:"+xmlDom.parseError.errorCode+ " 错误文件中的位置:"+xmlDom.parseError.filepos+ " 错误的行号:"+xmlDom.parseError.line+ " 错误的行的字符位置:"+xmlDom.parseError.linepos+ " 错误的原因:"+xmlDom.parseError.reason); } } } xmlDom.load("demo.xml");</span>
二、DOM2中的XML
DOM2 级在 document.implementaion 中引入了 createDocument()方法。 IE9、 Firefox、Opera、Chrome 和 Safari 都支持这个方法。
1、创建xmlDom对象
<span style="font-size:18px;">//创建一个xmlDom对象,并且向里面添加数据 //三个参数,第一个代表的是命名空间,第二个是根节点名,第三个是文档规范 var xmlDom = document.implementation.createDocument("",'root',null); alert(xmlDom); var user = document.createElement("user"); xmlDom.documentElement.appendChild(user); var txt = document.createTextNode("aaa"); user.appendChild(txt); alert(xmlDom.getElementsByTagName("user")[0].nodeType); // 1 alert(xmlDom.getElementsByTagName("user")[0].tagName); // user alert(xmlDom.getElementsByTagName("user")[0].firstChild.nodeValue); // aaa</span>
由于 DOM2 中不支持 loadXML()方法,所以,无法简易的直接创建 XML 字符串。所以,只能采用以上的做法,通过标准的DOM来实现xml元素 的添加
2、载入xml文件
DOM2级只是支持load()方法载入外部的xml文件,而且是同一台服务器下。也区分同步和异步,默认的是异步模式。但是不管同步还是异步,都只是Mozilla 的 Firefox 才能支持,新版本的opera也支持。
<span style="font-size:18px;">//异步模式,默认的 var xmlDom = document.implementation.createDocument("","root",null); xmlDom.async = true; xmlDom.load("demo.xml"); alert(typeof xmlDom.getElementsByTagName("user")[0]);//输出肯定为空 //使用load事件,并且这个事件支持通过事件注册函数addevent来添加,而且还支持this的传递 var xmlDom = document.implementation.createDocument("","root",null); xmlDom.async = true; xmlDom.onload =function(){ alert(this.getElementsByTagName("user")[0].tagName); } xmlDom.load("demo.xml")</span>
<span style="font-size:18px;">//同步模式 var xmlDom = document.implementation.createDocument("","root",null); xmlDom.async = false; xmlDom.load("demo.xml"); alert(typeof xmlDom.getElementsByTagName("user")[0].tagName);</span>
3、DomParse 方法
从上面看出,DOM2级中存在两个问题:不能够想IE中通过loadXML()方法来简单的创建XML文件;不能够像IE中那样通过.xml属性来讲XML文档进行一个序列化输出。故提供了 DOMParser 类型来创建 XML DOM 对象。新版本的浏览器都支持
<span style="font-size:18px;">//通过 new DOMParser() 来创建xmlDom对象,高版本的浏览器都支持 var xmlPar = new DOMParser(); alert(xmlPar); //object DOMParser var xmlStr = "<root> <user> aaa</user></root>"; try{ //如果上面的XML字符串在语法上有错误,比如开始结束标签不匹配。IE11在此处会抛异常。其它浏览器则不会 var xmlDom = xmlPar.parseFromString(xmlStr,"text/xml");//object XMLDocument alert(xmlDom); alert(xmlDom.getElementsByTagName("user")[0].tagName); //user }catch(e){ alert("XML语法出错,无法创建") }</span>
4、XMLSerializer 序列化字符串
通过domparse能够创建一个xmlDom对象,但是没有提供序列化的功能,所以就提供了一个XMLSerializer 类型来帮助序列化XML字符串
<span style="font-size:18px;"> //序列化XML字符 var xmlSer = new XMLSerializer(); var xml = xmlSer.serializeToString(xmlDom); //上面创建的xmlDom对象 alert(xml); </span>
5、异常捕获
和IE中一样,可能在XML解析的时候出现错误,但是在 DOM2 级处理 XML 发生错误时,并没有提供特有的对象来捕获错误,而是直接生成另一个错误的 XML 文档返回,通过这个文档可以获取错误信息,这个文档中有一个标签 parsererror 它里面保存了错误的信息,修改上面序列化中的代码如下
<span style="font-size:18px;"> //捕获错误,如果序列化错误会自动的生成另外一个XML文档 var xmlSer = new XMLSerializer(); var xml = xmlSer.serializeToString(xmlDom); //上面创建的xmlDom对象 var xmlError = xmlDom.getElementsByTagName("parsererror"); alert(xmlError.length); //0 错误为1,只有一条信息 if(xmlError.length > 1){ alert(xmlError[0].textContext); }else{ alert(xml) }</span>
PS:errors[0].firstChild.nodeValue 也可以使用 errors[0].textContent 来代替
三、跨浏览器处理XML
要实现跨浏览器就面对几个问题: 1.load()只有 IE、 Firefox、Opera 支持, 所以无法跨浏览器;2.获取 XML DOM 对象顺序问题,先判断先进的 DOM2 的,然后再去判断低版本的 IE;3.针对不同的 IE 和 DOM2 级要使用不同的序列化方式。4.针对不同的报错进行不同的报错机制。
为了兼容浏览器,下面的做法中没有提供加载XML文档的功能,只是实现了添加简单的XML字符串处理的操作,至于增加XML文件,方法都是通过标准的DOM处理
跨浏览器创建xmlDom对象
<span style="font-size:18px;">//创建xmlDom对象 function getXMLDom(xmlStr){ var xmlDom = null; //alert(typeof window.DOMParser) //function if(window.DOMParser instanceof Function){ //支持DOM2级 var xmlPar = new DOMParser(); try{ //用try来捕获异常,是因为在IE11中如果传递过来的XML字符串不正确会抛出异常 xmlDom = xmlPar.parseFromString(xmlStr,"text/xml"); }catch(e){ throw new Error("XML 格式不正确"); return; } var xmlError = xmlDom.getElementsByTagName("parsererror"); if(xmlError.length>0){ throw new Error("错误信息:"+xmlError[0].textContent); } }else if(window.ActiveXObject instanceof Function){ //低版本的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){ } } if(xmlDom!=null){ xmlDom.loadXML(xmlStr); if(xmlDom.parseError.errorCode != 0){ alert(xmlDom.parseError.reason) throw new Error("错误信息:"+xmlDom.parseError.reason); } }else{ throw new Error("您的浏览器或者操作系统不支持XML DOM对象"); return; } }else{ throw new Error("您的浏览器或者操作系统不支持XML DOM对象"); return; } return xmlDom; }</span>
跨浏览器序列化XML
<span style="font-size:18px;">//序列化方法 function serializerXml(xmlDom){ var xmlStr = ""; if(window.XMLSerializer instanceof Function){ //W3C var xmlSer = new XMLSerializer(); xmlStr = xmlSer.serializeToString(xmlDom); }else if(xmlDom.xml instanceof String){ //IE xmlStr = xmlDom.xml; } return xmlStr; }</span>
调用
<span style="font-size:18px;">//调用输出 var xmlStr = "<root> <user>aaa</user> </root>"; var xmlDom = getXMLDom(xmlStr); //alert(xmlDom); alert(serializerXml(xmlDom));</span>