不同浏览器对XML的解析问题(解决了多天困扰的问题)

[转自]http://book.51cto.com/art/200806/75667.htm
 
第11章  Ajax标准通信载体——XML语言
XML语言可以有复杂的层次性结构,因此它可以表示任意复杂的数据信息。同时XML是用纯文本编写的,便于人工阅读和修改。在Ajax中,XML起着很重要的作用,本章将集中讲解XML的相关知识。
11.1  XML概述
XML是Extensible Markup Language(取Extensible的X)的缩写,即可扩展标记语言。它是W3C提出的一种用来描述数据的标准语言。它的通性使开发人员无需为每个应用程序都单独设计一种数据表示,这给不同类型程序间的数据通信带来了很大的方便。比如Web服务通信中的SOAP协议使用的就是XML语言,网络上到处可见的RSS也是基于XML的。
11.1.1  一个简单的XML实例
从某种程度上说,XML与HTML的语法相当类似,它们都是由文本以及包含文本的标记组成的。下面是一段简单的HTML代码,其中<u>和<i>标记分别表示下划线显示和斜体显示效果,代码如下:
<dl>
<dt><u>标题</u></dt>
<dd><i>条目1</i></dd>
<dd><i>条目2</i></dd>
<dd><i>条目3</i></dd>
</dl>
运行这段代码的效果如图11.1所示。
下面是一段内容类似的XML代码:
<?xml version="1.0" encoding="GB2312"?>
<items>
<title>标题</title>
<item>条目1</item>
<item>条目2</item>
<item>条目3</item>
</items>
运行这段程序的效果如图11.2所示。
对比以上两段代码,可以发现几个不同点。一是HTML中的标记都是特定的,每个标记都有特定含义;而XML中的标记则完全可以由用户自己定义。二是HTML的标记主要用于说明数据的显示方式,比如下划线、斜体、列表等;而XML只关心数据的内容,不关心数据如何显示。
提示:事实上,W3C规定的另外一个标准XSLT可以为XML指定显示方式。
11.1.2  XML的基本概念
按照W3C的DOM模型,XML中的基本单位称为结点(Node),结点可以有很多种类型,比如文档(Document)、元素(Element)、属性(Attribute)、文本(Text)等。
文档结点是整个文档最上层的结点,即根结点;元素结点可以包含子结点,子结点可以是属性、文本甚至一个元素,即元素是可以嵌套的。比如上面的例子中,“<title>标题</title>”是一个元素,整个“<items>……</items>”也是一个元素。其中title这个元素的内容“标题”就是一个文本结点,它在DOM模型中作为title的子结点出现。整个XML文档就形成了一个结点树的结构。
而元素结点的标识是由标记(Tag)来完成的。XML中的标记是由小于号(<)开始,以大于号(>)结束。对于标记内容来说,若以斜杠(/)开头,则认为这是一个结束标记,表示一个元素的结束。否则这个标记就是一个开始标记,表示一个元素的开始。同时,在标记内也可以附加一些属性(Attribute),代码为:
<item name="itemname"></item>
这里,为item标记附加了一个name属性,其值为itemname。
注意:有些标记不包含任何内容,可以直接在开始标记的结束符号(大于号)前加一个斜杠表示该标记的结束,如上面属性的例子可以写为:<item name="itemname"/>
在编写XML的时候,需要特别注意以下几个问题。
• XML标记是区别大小写的,这与HTML不同;
• XML的属性值一定要加引号(单引或双引),而HTML中可以忽略;
• XML中的所有标记都要“有始有终”,而HTML中的某些标记可以没有结束标记,如<br>、<img>;
• “<![CDATA[”和“]]>”可以用来包含有特殊字符(如大于号小于号)的文本,如<![CDATA[<item>text</item>]]>在XML就表示<item>text</item>这一段文本(注意:这里的item因为被CDATA包含,已经不是一个标记了)。
11.1.3  XML在Ajax中的作用
XML的优势在于其通用性,和较强的表达能力,因此用XML作为数据交换的一个标准格式无疑具有一定的吸引力。Ajax中客户端与服务器端之间的通信也可以采用这种办法。
实际上,在Ajax最初的定义中,XML是作为一个客户端与服务器端的通信载体出现的。例如可以将客户端对服务器端的请求用XML包装起来,也可以将客户端提交的一个表单内容转化成一个XML片段传给服务器端进行处理。下面的XML片段可以代表一个注册表单提交的内容,代码如下:
<id>Richard</id>
<password>pass123</password>
<sex>男</sex>
<email>[email][email protected][/email]</email>
<address>南京大学鼓楼校区</address>
在Ajax中使用XML的另一个原因是,它可以进一步降低客户端和服务器端的耦合性。由于XML的中立性,客户端和服务器端使用的开发语言、平台等一些细节都有自由选择的空间,这与客户端服务器架构的理念是一致的,即客户端和服务器端应当相对独立。
11.2  使用Javascript操作XML
11.2.1  MSXML介绍
在IE中,微软提供了MSXML控件来支持XML操作。每个系统安装的MSXML版本都有可能不同,因此在编写程序的时候要注意版本的确定。目前MSXML从旧到新有以下几个版本。
• Microsoft.XmlDom (最初的版本)
• MSXML2.DOMDocument
• MSXML2.DOMDocument.3.0
• MSXML2.DOMDocument.4.0
• MSXML2.DOMDocument.5.0
下面的函数用来获取一个系统中可用的最新版本的DomDocument对象,若都不存在,则返回空,代码如下:
function createDomDoc()
{
var signatures=["Msxml2.DOMDocument.5.0",
"Msxml2.DOMDocument.4.0",
"Msxml2.DOMDocument.3.0",
"Msxml2.DOMDocument",
"Microsoft.XmlDom"];
for(var i=0;i<signatures.length;i++)
{
try{
var domDoc = new ActiveXObject(signatures[i]);
return domDoc;
} catch(e) {
//忽略错误,继续循环尝试下一个版本
}
}
return null;
}
11.2.2  在IE浏览器中创建XML片段
下面介绍如何通过DomDocument对象创建一个XML片段,以11.1.3节中的XML片段为例。下面是代码,返回的则是XML文本。
function createXml(doc)
{
var root = doc.createElement("items");
var title = doc.createElement("title");
title.text = '标题';
root.appendChild(title);    //添加title子结点
for(var i=0;i<3;i++)
{
var item = doc.createElement("item");
item.text = '条目'+i;
root.appendChild(item);   //添加item子结点
}
doc.appendChild(root);    //设置root结点
return doc.xml;
}
在这段代码中,createElement方法用来建立一个新的元素结点,参数是这个结点标记的名称。结点的text属性可以用来设置其内容(严格说,就是设置其文本子结点的内容)。
appendChild方法可以在某个元素结点下添加子结点。在创建XML片段的时候,需要使用该方法将构建好的XML附加到DomDocument对象上。XML属性则可以将DomDocument序列化,使其成为一段文本。
在程序中加入下列执行代码:
var domDoc = createDomDoc();
if(domDoc!=null)
{
var xml=createXml(domDoc);
alert(xml);
}
else
{
alert('未安装MSXML控件');
}
运行结果如图11.3所示。
图11.3  通过XML DOM对象创建的XML文本
11.2.3  在IE浏览器中解析XML文本
下面介绍如何通过DomDocument对象来解析已有的XML文本。在IE中有2种导入XML文本的办法。一种是通过load方法,其语法代码如下。
domDoc.load("test.xml"); //导入test.xml文件中的内容
该方法只能导入JavaScript所在的本地文件,而不支持远程导入。
第二种方法是通过loadXML方法直接导入,代码是:
xml = "";
domDoc.loadXML(xml); //直接解析xml中包含的XML文本
另外,由于解析XML有时会花费较长的时间,MSXML还提供了异步机制,代码如下:
domDoc.async = true; //异步调用
domDoc.onreadystatechange = function ()  //触发函数
{
if(domDoc.readyState==4) {
doc = domDoc;
document.write("
");
var root = doc.documentElement;
document.write("
"+root.childNodes.item(0).text+"");
for(var i=1;i{
node = root.childNodes.item(i);
document.write("
"+node.text+"");
}
document.write("
");
}
};
domDoc.loadXML(xml);
通过这段代码可以看出:设置DomDocument的async属性可以进行异步解析;childNodes属性含有一个包含所有子结点的列表,可以通过childNodes.item(i)来获取索引值为i的子结点;通过childNodes的length属性可以获得子结点的个数;结点的text属性则可以用来获取某个元素结点的内容(或者说是其文本子结点的内容)。
另外,触发函数中的readyState取值有其特定的含义。
• 1:表示正在载入,还没开始解析;
• 2:表示数据已经载入完毕,开始解析,但是DOM仍不可用;
• 3:表示一些数据已经解析完成,DOM已经可以使用,但还不完整,只包含已解析部分的数据。另外,此时的DOM是只读的;
• 4:表示数据解析已经全部完成,DOM已经可以正常使用。
运行这段代码,将会得到如图11.4所示的效果。
通过运行的结果可以看出,触发函数生成的实际上就是本章最开始给出的HTML代码。
11.2.4  使用Mozilla浏览器支持的DOM模型创建XML片段
在基于Mozilla的浏览器中(如Mozilla、Firefox、Netscape 6+等),对XML操作的支持是集成在浏览器内部的,这与IE浏览器使用外部控件的方式不同。
Mozilla浏览器的DOM模型对XML的操作方式也与IE浏览器有所不同,IE浏览器中的XML操作均集成在DomDocument对象中,而Mozilla浏览器中对XML的操作主要可以分为3个对象。
• Document对象:主要存放XML的数据及其结构;
• XMLSerializer对象:负责Document对象的序列化;
• DOMParser对象:负责解析XML文本。
创建一个XML片段,代码如下:
function createXml(doc)
{
var root = doc.createElement("items");
var title = doc.createElement("title");
title.textContent = '标题'; //注意Mozilla浏览器中使用的是textContent属性
root.appendChild(title);
for(var i=0;i<3;i++)
{
var item = doc.createElement("item");
item.textContent = '条目'+i;
root.appendChild(item);
}
doc.appendChild(root);
var serializer = new XMLSerializer(); //序列化方式也与IE浏览器中不同
var xml = serializer.serializeToString(doc);
return xml;
}
var domDoc = document.implementation.createDocument("", "", null); //创建Document对象
var xml=createXml(domDoc);
alert(xml);
Document对象的创建是通过createDocument来进行的,createDocument是DOM模型规定的一个标准方法,它的原型代码如下:
Document createDocument(in DOMString namespaceURI,
in DOMString qualifiedName,
in DocumentType doctype) raises(DOMException);
其中namespaceURI为文档的名字空间,qualifiedName是文档的限定名,docType则是文档的类型。一般情况下全部传空值就可以了。上述代码在Firefox浏览器运行的结果如图11.5所示。
图11.5  Firefox浏览器下运行XML DOM对象
可以看出,在Mozilla浏览器中对XML的操作实际上跟IE浏览器中是类似的,两者均是W3C的DOM模型的一个实现。但是两者也有一些不同。
• 一是IE浏览器中结点的text属性在Mozilla浏览器中变成了textContent属性;
• 二是序列化。IE浏览器中的序列化简单地使用xml属性即可,Mozilla浏览器中则需要引入XMLSerializer对象。
为了提高可移植性,可以不使用text或textContent属性,而利用文本子结点来创建XML片段,代码如下:
var text = doc.createTextNode("条目"+i);
item.append(text);
11.2.5  使用Mozilla浏览器支持的DOM模型解析XML片段
下面介绍在Mozilla浏览器中解析XML的方式。
function traverse(doc)
{
document.write("<dl>");
var root = doc.documentElement;
document.write("<dt><u>"+root.childNodes.item(0).textContent+"</u></dt>");
for(var i=1;i<root.childNodes.length;i++)
{
node = root.childNodes.item(i);
document.write("<dd><i>"+node.textContent+"</i></dd>");
}
document.write("</dl>");
}
var parser = new DOMParser();
var doc = parser.parseFromString(xml, "text/xml");
traverse(doc);
这段解析XML的代码与IE浏览器中的也非常类似,其运行结果如图11.6所示。
图11.6  Firefox浏览器运行XML DOM对象
11.2.6  基于Javascript的XML解析程序
在IE浏览器和Mozilla浏览器中操作XML的2种办法都是基于DOM模型的,功能都相当强大,然而2个平台上的可移植性却是个问题。可以通过JavaScript开发一个解析程序,来解决这个可移植性的问题。在国外,已经有人写出了类似的程序,称做xparse。
提示:关于xparse,可以在[url]http://jeremie.com/Dev/XML/[/url]找到其主页。
下载xparse以后可以直接使用,但是要注意,xparse只提供读操作。下面是一段解析XML文本的示例代码,这段代码在IE浏览器和Firefox浏览器中均运行正常。
<script src="xparse.js"></script>
<script>
var xml = "<items><title>标题</title><item>条目0</item><item>条目1</item><item>条目2</item></items>";
var tag = Xparse(xml);
var root = tag.contents[0];
document.write("<dl>");
document.write("<dt><u>"+root.contents[0].contents[0].value+"</u></dt>");
for(var i=1;i<root.contents.length;i++)
{
document.write("<dd><i>"+root.contents[i].contents[0].value+"</i></dd>");
}
document.write("</dl>");
</script>
运行后可以发现运行结果与前面2种方法解析的完全相同。
xparse并不遵循DOM规范,因此使用起来比较困难,可读性也较差。contents属性相当于DOM中的childNodes属性,每个结点常用的属性包括value和type,分别表示结点的值和类型。
注意:xparse没有提供类似text或者textContent的属性,因此代码中使用2个连续的contents来取得文本子结点。
xparse解决了移植性问题,但是由于它是使用JavaScript编写的,与IE和Mozilla的二进制代码相比,其效率必然较低。而且,由于其缺乏对DOM规范的支持,使用很不方便。但对于Ajax而言,在传输的数据量都不会太大的情况下,xparse仍然不失为一种好的解决方案。
 

0

收藏

phelpszy

31篇文章,6W+人气,0粉丝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章