自制XML解析器源码分析

自制XML解析器源码分析

首先,我们确定一下需求:

(1)我们希望它能把XML字符串解析成JSON对象。

(2)至少能兼容FireFox和IE。

(3)这个工具类最好是单例的。

代码:

/**

* 把XML解析成JSON对象

* 既可以直接解析字符串拼接成的XML

* 也可以解析通过Ajax请求获得的XML文件对象

* 主要兼容:IE、FireFox、Opera,其他浏览器中不能运行

* @author 大漠穷秋

* @since 2010-12-24

* @ver 1.0

*/

XmlParser=(function(){

functioncreateXmlDocument(){

vardoc=null;

if(Ext.isIE){

var docIDs = [

"Msxml2.DOMDocument.6.0",

"Msxml2.DOMDocument.5.0",

"Msxml2.DOMDocument.4.0",

"Msxml2.DOMDocument.3.0",

"MSXML2.DOMDocument",

"MSXML.DOMDocument"

];

for(var i=0;i<docIDs.length;i++){

//尝试为IE创建XMLDocument对象

try{

doc=newActiveXObject(docIDs[i]);

return doc

}catch(e){}

}

throw new Error("无法为你的浏览器创建XMLDocument对象。");

}else{//FireFox,Opera,Safari,Chrome

doc= document.implementation.createDocument ("","",null);

}

returndoc;

};

this.stack=[];

function_preParse(xmlNode){

if(xmlNode.childNodes){

for(vari=0;i<xmlNode.childNodes.length;i++){

varnode=xmlNode.childNodes[i];

varnodeType=node.nodeType;

if(nodeType==1){ //复合节点

varobj={};

obj.name=node.tagName;

obj.children=[];

stack.push(obj);

_preParse(node);

if(nodeType==1){

vartop=stack.pop();

if(stack.length==0){

returntop;

}

vartop2=stack[stack.length-1];

top2.children.push(top);

}

}elseif(nodeType==3){ //文本节点

//过滤掉空行、回车、制表符

vartext=null;

if(Ext.isIE){

text=node.text;

}else{

text=node.textContent;

}

varresult=text.replace(/(\r|\n|\t|\s)/g, '');

if(result){

vartop=stack[stack.length-1];

deletetop.children;

top.value=result;

}

}

}

}

}

this.xmlDoc=createXmlDocument();

functionxmlToJSON(xmlNode){

stack=[];

varresult=_preParse(xmlNode);

returnresult;

};

return {

/**

* 解析xmlObj并返回JSON对象

*/

parse:function(xmlObj){

if(!xmlObj){

returnnull;

}

/**

* 直接解析字符串拼接成的XML

* 尚未实现

*/

if(Ext.type(xmlObj)=='string'){

thrownew Error("直接解析字符串的功能尚未实现。");

returnnull;

}

returnxmlToJSON(xmlObj);

},

/**

* 解析Ajax加载的XML文件

*/

parseResponse:function(response){

if(!response||!response.responseXML){

thrownew Error("无法读取响应数据,response为null或没有xml
数据。");

returnnull;

}

returnthis.parse(response.responseXML);

},

/**

* 直接加载远程XML文件

* 貌似有缓存问题

*/

loadXml:function(filePath,fn){

if(Ext.isIE){

xmlDoc.onreadystatechange=function(){

if(xmlDoc.readyState==4){//XML文档已经加载完毕

fn(xmlDoc);

}

}

}else{

xmlDoc.οnlοad=fn.createCallback(xmlDoc);

}

xmlDoc.load(filePath);

},

/**

* 获取当前正在解析的XMLDocument对象

*/

getDocObj:function(){

returnxmlDoc;

}

}

})();

源码解析:

约定一些解析规则:

(1)XML的标签名变成对象的name属性。

比如:

<name>大漠穷秋1</name>

会被解析成:

{name:'name',value:'大漠穷秋1'}

(2)如果标签存在子节点,将自动创建一个children属性,用来存储子对象。

比如:

<skill>

<name>Java</name>

<year>4</year>

</skill>

会被解析成:

{name:'skill',children:[

{name:'name',value:'Java'},

{name:'year',value:'4'}

]}

在XmlParser中,核心工具函数是createXmlDocument()、_preParse()这两个。

createXmlDocument看起来比较烦琐,其实没有技术含量,主要完成根据不同浏览器创建出XmlDocument对象的任务。与Ajax中的XmlHttpRequest对象类似,XmlDocument这个对象依赖于具体浏览器的实现。在不同的浏览器中,创建XmlDocument的方式不同,并且最终暴露出来的属性和方法都有很大的差别。

作为Ext的超级粉丝,我们见识了大量的JS技巧和框架设计思想,想象一下,你也是Ext框架开发团队中的一员,你会如何去设计这个解析工具?秉承Ext一贯的思路和手法,必须先把浏览器的差异屏蔽掉,然后对上层调用代码暴露一组通用的编程接口,这就是createXmlDocument这个函数存在的理由。

XML是一种“树形”的结构,它拥有唯一的根节点,然后标签可以层层嵌套。只要注意标签的配对、关闭及不能出现特殊的符号等,其他并没有特别的限制。那么在解析过程中必然会涉及树节点的遍历问题,这里使用的解析方法是:前序遍历栈存储节点递归3个手段结合。

在这个过程中,最核心的一个手法是使用一个数组做栈,在递归的过程用来存储访问过的节点。当发现某个节点还有子节点的时候,就把这个节点做成这样一个对象入栈:{name:'节点标签名',value:'',children[]},然后继续访问它的第一个孩子节点。当发现某个节点已经是简单节点(没有子节点),则把它“做成”JSON对象,然后弹出栈顶元素,把简单节点对应的JSON对象插入弹出元素的children数组中去。

核心的思路如上所述,如图7-28所示,但是纯文字描述必然比较抽象,请读者自行使用Firebug跟踪以上过程。

——本段文字节选自《EXT江湖》

图书详细信息:http://blog.csdn.net/broadview2006/article/details/7211734

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值