• 自制XML解析器源码分析




     

    --

    自制XML解析器源码分析

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

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

    2)至少能兼容FireFoxIE

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

    代码:

    /**

     * XML解析成JSON对象

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

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

     * 主要兼容:IEFireFoxOpera,其他浏览器中不能运行

     * @author 大漠穷秋

     * @since 2010-12-24

     * @ver 1.0

     */

    XmlParser=(function(){

        function createXmlDocument(){

            var doc=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=new ActiveXObject(docIDs[i]);

                     return doc

                 }catch(e){}

             }

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

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

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

            }

            return doc;

        };

       

        this.stack=[];

        function _preParse(xmlNode){

            if(xmlNode.childNodes){

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

                    var node=xmlNode.childNodes[i];

                    var nodeType=node.nodeType;

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

                        var obj={};

                        obj.name=node.tagName;

                        obj.children=[];

                        stack.push(obj);

                        _preParse(node);

                        if(nodeType==1){

                            var top=stack.pop();

                            if(stack.length==0){

                                return top;

                            }

                            var top2=stack[stack.length-1];

                            top2.children.push(top);

                        }

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

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

                        var text=null;

                        if(Ext.isIE){

                            text=node.text;

                        }else{

                            text=node.textContent;

                        }

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

                        if(result){

                            var top=stack[stack.length-1];

                            delete top.children;

                            top.value=result;

                        }

                    }

                }

            }

        }

       

        this.xmlDoc=createXmlDocument();

       

        function xmlToJSON(xmlNode){

            stack=[];

            var result=_preParse(xmlNode);

            return result;

        };

       

        return {

            /**

             * 解析xmlObj并返回JSON对象

             */

            parse:function(xmlObj){

                if(!xmlObj){

                    return null;

                }

                /**

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

                 * 尚未实现

                 */

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

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

                    return null;

                }

                return xmlToJSON(xmlObj);

            },

            /**

             * 解析Ajax加载的XML文件

             */

            parseResponse:function(response){

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

                    throw new Error("无法读取响应数据,responsenull或没有xml
                       
    数据。");

                    return null;

                }

                return this.parse(response.responseXML);

            },

            /**

             * 直接加载远程XML文件

             * 貌似有缓存问题

             */

            loadXml:function(filePath,fn){

                if(Ext.isIE){

                    xmlDoc.onreadystatechange=function(){

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

                            fn(xmlDoc);

                        }

                    }

                }else{

                    xmlDoc.onload=fn.createCallback(xmlDoc);

                }

                xmlDoc.load(filePath);

            },

            /**

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

             */

            getDocObj:function(){

                return xmlDoc;

            }

        }

    })();

    源码解析:

    约定一些解析规则:

    1XML的标签名变成对象的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://www.cnblogs.com/broadview/archive/2012/01/20/2327735.html

     

  • 相关阅读:
    《软件项目成功之道》阅读笔记02
    每日日报47
    每日日报46
    每日日报45
    WAMPSERVER打开phpmyadmin时遇到404错误——解决办法
    每日日报44
    每日日报43
    简单的利用Layui来实现登录功能
    01函数式编程概念
    03适配器模式
  • 原文地址:https://www.cnblogs.com/broadview/p/2343123.html
Copyright © 2020-2023  润新知