码迷,mamicode.com
首页 > 其他好文 > 详细

自个倒腾的解析XML格式字符串的类【具备一定的通用性】

时间:2016-05-27 07:03:53      阅读:240      评论:0      收藏:0      [点我收藏+]

标签:

    在公司写了很多解析xml字符串的代码,之前都是不同的结构都是写一段这个结构的DOM操作的代码来获取数据。写了几十个,每个几十行代码,又烦!

    一直想写一个比较通用一点的类,这几天有了一点想法,直接上代码了!没有考虑兼容性,也没有在项目中使用,就当作工作总结了,欢迎大家提意见。

 

<!DOCTYPE HTML>
<head>
    <meta charset="utf-8">
    <title>适用的xml字符串解析</title>
    <script type="text/xml" id="xml-str1">
        <result>
            <tasks date="2016-09-01" count="2">
                <task name="1" id="1">任务1</task>
                <task name="2" id="2">任务2</task>
            </tasks>
            <tasks date="2016-09-05" count="3">
                <task name="3" id="3">任务3</task>
                <task name="4" id="4">任务4</task>
                <task name="5" id="5">任务5</task>
            </tasks>
        </result>
    </script>
    <script type="text/xml" id="xml-str2">
        <result>
            <tasks date="2016-10-01" count="2">
                <task name="11" id="11">任务11111</task>
                <task name="22" id="22">任务22222</task>
            </tasks>
            <tasks date="2016-10-05" count="3">
                <task name="33" id="33">任务33333</task>
                <task name="44" id="44">任务44444</task>
                <task name="55" id="55">任务55555</task>
            </tasks>
        </result>
    </script>
    <script type="text/xml" id="xml-str-struct1">
        <result data-struct-type="[]">
            <tasks data-parse-attr="true" data-struct-type="{}" data-children-type="[]">
                <task data-parse-attr="true" data-struct-type="{}" data-parse-node-value="true"></task>
            </tasks>
        </result>
    </script>
    
    <!-- <script src="./XmlParse.js"></script> -->
    <script>
        function parseXmlToDoc(xml) {    
            return new DOMParser().parseFromString(xml, text/xml);
        }
        var XmlParse = (function () {    
            //私有函数
            //获取xml document对象的根元素
            var _getDocEl = function (doc) {
                return doc.documentElement;
            };
            //获取某个元素下所有子元素
            var _getElements = function (el) {
                return el.getElementsByTagName(*);
            };
            //获取节点的nodeValue
            var _getNodeValue = function (node) {
                var child = node.firstChild;
                
                return child.nodeType === 3 ? child.nodeValue : ‘‘;
            };
            //附近数据到指定节点, 只允许附加一次
            var _appendNodeData = function (node, data) {
                if (!node.___$data$___) {
                    node.___$data$___ = data;
                }
            };
            //获取附加到指定节点的数据
            var _getNodeData = function (node) {
                return node.___$data$___;
            };
            //将子节点的数据附近到父节点数据上
            var _appendChildDataToParentData = function (childData, parentData, attrName) {        
                if (parentData instanceof Array) {
                    parentData.push(childData);
                } else if (Object.prototype.toString.call(parentData) === [object Object]) {
                    if (parentData.children) {
                        parentData.children.push(childData);
                    } else {
                        parentData[attrName] = childData;
                    }
                }
            };
            //判断数据类型是否数组
            var _dataTypeIsArray = function (dataType) {
                if (dataType === []) {
                    return true;
                } 
                return false;
            };
            //判断数据类型是否对象字面量
            var _dataTypeIsJson = function (dataType) {
                if (dataType === {}) {
                    return true;
                } 
                return false;
            };
            //根据数据类型, 生成对应的数据引用或变量
            var _createDataByType = function (dataType) {
                var data;
                
                if (_dataTypeIsArray(dataType)) {
                    data = [];
                } else if (_dataTypeIsJson(dataType)) {
                    data = {};            
                } else {
                    data = ‘‘;
                }
                
                return data;
            };
            

            //构造函数
            /*
            @param {String struct} 映射要解析的xml格式的字符串的数据结构, 使用自定义属性定义数据类型等, 类似:
                <result data-struct-type="[]">
                    <tool data-struct-type="{}" data-parse-attr="true"/>
                </result>
            @desc:
                data-struct-type: 代表解析这个节点的数据类型, 其值是[], {}或‘‘, 分别代表数组, 对象字面量及字符串, 默认是字符串
                data-parse-node-value: 代表是否解析这个节点的nodeValue, 值是true则解析, 其他均不解析, 默认不解析
                data-parse-attr: 代表是否解析这个节点的属性, 值是true则解析, 其他均不解析, 默认不解析
                data-attr-name: 代表一个节点数据需要存储为父节点数据的属性值时, 对应的属性名, 没有赋值的话默认是节点名称
                data-children-type: 代表一个节点数据是一个对象字面量时, 使用一个children属性存储子节点的数据, 目前应固定给值为[]
                {String struct}解析完得到一个对象字面量, 类似:
                    //0代表节点的层级
                    {
                        0: {
                            //result是节点名
                            ‘result‘: {
                                dataType: ‘[]‘,
                                isParseAttrs: false,
                                isParseNodeValue: false,
                                childrenDataType: false,
                                attrName: ‘xxx‘
                            }
                        }
                    }
                如果一个节点需要解析属性或节点值时, 同时又要存储其子节点的数据, 那么应该这样组合表示
                <nodeName data-parse-attr="true" data-parse-node-value="true" data-struct-type="{}" data-children-type="[]"></nodeName>
                类似:
                数据结构:
                <tasks data-parse-attr="true" data-struct-type="{}" data-children-type="[]">
                    <task data-parse-attr="true" data-struct-type="{}"/>
                </tasks>
                xml数据:
                <tasks date="2016-09-01" count="2">
                    <task name="1" id="1"/>
                    <task name="2" id="2"/>
                </tasks>
                解析出数据:
                {
                    date: "2016-09-01",
                    count: "2",
                    children: [{
                        name: "1",
                        id: "1"
                    }, {
                        name: "2",
                        id: "2"
                    }]
                }
            */
            /*
            @param {String xmlStr} 需要解析的xml格式的字符串, 必须是符合xml格式的
            */
            /*
            @param {Object config} 配置对象字面量, 可配置标识数据结构的字段, 一旦配置后, 
                就可以使用自定义的字段作为数据结构节点的属性名了, 在和数据中定义的属性冲突时特别有用
                {
                    nodeValueAttr: ‘‘,  //节点值作为属性值存储时的属性名, 默认是nodeValue
                    type: ‘‘,       //标识数据的类型, 默认是data-struct-type
                    attr: ‘‘,       //标识是否解析属性, 默认是data-parse-attr
                    value: ‘‘,      //标识是否解析节点值, 默认是data-parse-node-value
                    children: ‘‘,   //标识是否需要以children属性存储子节点数据, 默认是data-children-type
                    attrName: ‘‘    //标识节点的数据作为父节点数据的属性值存储时对应的属性名, 默认是data-attr-name
                }
            */
            var constructor = function (struct, xmlStr, config) {
                
                var structDoc = parseXmlToDoc(struct),
                    xmlDoc = parseXmlToDoc(xmlStr),
                    key;
                            
                //伪私有变量
                this._configs = {
                    nodeValueAttr: nodeValue,
                    type: data-struct-type,
                    attr: data-parse-attr,
                    value: data-parse-node-value,
                    children: data-children-type,
                    attrName: data-attr-name
                };
                this._levelNodes = {length: 0};
                this._data = null;
                this._struct = {
                    0: {}
                };
                
                if (config) {
                    for (key in config) {
                        this._configs[key] = config[key];
                    }
                }    
                
                this._structRootEl = _getDocEl(structDoc);
                
                this._rootEl = _getDocEl(xmlDoc);        
                this._elements = _getElements(this._rootEl);        
                
            };
            //实例方法, 目前只对外提供一个实例方法, 需要使用的方法和变量基本都私有化, 减少与外界的联系
            //自顶层到底层进行解析
            /*
            @return {Object}
            */
            constructor.prototype.parse = function () {
                if (this._data) {
                    return this._data;
                }
                
                var i = 1,
                    iLen;
                    
                this._parseRootStruct();
                this._parseNodesStruct();
                
                this._parseRootNode();
                this._parseNodesLevel();
                
                iLen = this._levelNodes.length;
                
                for (; i <= iLen; i++) {
                    var nodes = this._levelNodes[i],
                        j = 0,
                        jLen = nodes.length;
                        
                    for (j; j < jLen; j++) {
                        var node = nodes[j];
                        this._parseNode(node);
                    }
                    
                }
                
                return this._data;
                
            };
            //伪私有函数
            //解析根节点, 赋值给this._data私有变量
            constructor.prototype._parseRootNode = function () {
                var nodeName = this._rootEl.nodeName,
                    struct = this._struct[0][nodeName],
                    dataType = struct.dataType;
                //根节点一般都是[]或{}    
                if (dataType === []) {
                    _appendNodeData(this._rootEl, []);
                } else if (dataType === {}) {
                    _appendNodeData(this._rootEl, {});
                }
                this._data = _getNodeData(this._rootEl);
            };
            //解析单个节点, 并附加到父节点上
            constructor.prototype._parseNode = function (node) {
                var parentNode = node.parentNode,
                    parentData = _getNodeData(parentNode),
                    nodeStruct = this._getNodeStruct(node),
                    nodeData;
                //解析子节点的数据    
                this._parseNodeData(node, nodeStruct);
                //获取子节点解析出的数据
                nodeData = _getNodeData(node);
                
                _appendChildDataToParentData(nodeData, parentData, nodeStruct.dataAttrName);
            };
            
            //根据子节点对应的数据结构, 解析出子节点的数据, 并附加到子节点上
            constructor.prototype._parseNodeData = function (node, struct) {
                var dataType = struct.dataType,
                    isParseAttrs = struct.isParseAttrs,
                    isParseNodeValue = struct.isParseNodeValue,
                    data = _createDataByType(struct.dataType);

                if (struct.childrenDataType) {
                    data.children = [];
                } 
                
                if (isParseAttrs) {
                    this._parseNodeAttrsData(node, data);
                }
                
                if (isParseNodeValue) {
                    if (_dataTypeIsJson(dataType)) {
                        data[this._configs.nodeValueAttr] = _getNodeValue(node);
                    } else if (data === ‘‘) {
                        data = _getNodeValue(node);            
                    }
                }
                
                _appendNodeData(node, data);
            };
            //解析节点的属性值, 以键值的形式存进节点数据的引用, 键为属性名, 值为属性值
            constructor.prototype._parseNodeAttrsData = function (node, data) {
                var attrs = node.attributes,
                    i = 0,
                    iLen = attrs.length;
                    
                for (; i < iLen; i++) {
                    var attr = attrs[i],
                        name = attr.name,
                        value = attr.value;
                        
                    data[name] = value;
                }
            };
            //获取节点对应的数据结构
            constructor.prototype._getNodeStruct = function (node) {
                var level = this._parseNodeLevel(node, this._rootEl);
                    
                return this._struct[level][node.nodeName];
            };
            //解析根节点的数据结构
            constructor.prototype._parseRootStruct = function () {
                this._struct[0][this._structRootEl.nodeName] = this._parseNodeStruct(this._structRootEl);
            }; 
            //解析所有节点的数据结构
            constructor.prototype._parseNodesStruct = function () {
                var eles = _getElements(this._structRootEl),
                    i = 0,
                    iLen = eles.length;
                    
                for (; i < iLen; i++) {
                    var node = eles[i],
                        nodeName = node.nodeName,
                        level = this._parseNodeLevel(node, this._structRootEl),
                        struct = this._parseNodeStruct(node);
                        
                    this._struct[level] || (this._struct[level] = {});
                    //认为对于同一层级相同节点名称的节点, 其数据结构应该是一样的
                    this._struct[level][nodeName] || (this._struct[level][nodeName] = struct);
                }
            };
            //解析单个节点的数据结构
            constructor.prototype._parseNodeStruct = function (node) {
                var dataType = this._getNodeDataType(node),
                    childrenDataType = this._getNodeChildrenDataType(node),
                    isParseAttrs = this._isParseNodeAttrs(node),
                    isParseNodeValue = this._isParseNodeValue(node),
                    dataAttrName = this._getNodeDataAttrName(node);
                    
                    return {
                        dataType: dataType,
                        childrenDataType: childrenDataType,
                        isParseAttrs: isParseAttrs,
                        isParseNodeValue: isParseNodeValue,
                        dataAttrName: dataAttrName
                    };
            };
            //解析所有的节点, 将节点相对于根节点分成一层一层的, 存入私有变量this._levelNodes
            constructor.prototype._parseNodesLevel = function () {
                var i = 0,
                    iLen = this._elements.length,
                    maxLevel = 0;
                    
                for (; i < iLen; i++) {
                    var node = this._elements[i],
                        level = this._parseNodeLevel(node, this._rootEl);
                        
                    this._levelNodes[level] || (this._levelNodes[level] = []);
                    this._levelNodes[level].push(node);
                        
                    if (level > maxLevel) {
                        maxLevel = level;
                    }
                }
                //记录一个最大的层级
                this._levelNodes.length = maxLevel;
                    
            };
            //解析单个节点相对于根节点的层级
            constructor.prototype._parseNodeLevel = function (node, rootNode) {
                var parentNode = node,
                    level = 0;
                    
                while (parentNode !== rootNode) {
                    level++;
                    parentNode = parentNode.parentNode;
                }
                
                return level;
            };
            //获取节点数据作为父节点数据的一个属性值存储时, 对应的属性名, 默认是节点名称
            constructor.prototype._getNodeDataAttrName = function (node) {
                var name = node.getAttribute(this._configs.attrName);
                return name ? name : node.nodeName;
            };
            //获取节点数据的数据类型, 默认是字符串
            constructor.prototype._getNodeDataType = function (node) {
                var type = node.getAttribute(this._configs.type);
                return type ? type : ‘‘;
            };
            //获取节点数据存在children属性时, children的数据类型, 目前认定是数组
            constructor.prototype._getNodeChildrenDataType = function (node) {
                return node.getAttribute(this._configs.children) === [];
            };
            //判断是否需要解析节点的属性和属性值
            constructor.prototype._isParseNodeAttrs = function (node) {
                return node.getAttribute(this._configs.attr) === true;
            };
            //判断是否需要解析节点的nodeValue
            constructor.prototype._isParseNodeValue = function (node) {
                return node.getAttribute(this._configs.value) === true;
            };    
            
            
            
            return constructor;
        } ());
    </script>
    <script>
        var xmlStr1 = document.getElementById(xml-str1).textContent;
        var xmlStr2 = document.getElementById(xml-str2).textContent;
        var xmlStrStruct1 = document.getElementById(xml-str-struct1).textContent;
        var parser1 = new XmlParse(xmlStrStruct1, xmlStr1, {
            nodeValueAttr: desc
        });
        var parser2 = new XmlParse(xmlStrStruct1, xmlStr2);
        var data1 = parser1.parse();
        var data2 = parser2.parse();
        console.log(data1);
        console.log(data2);
    </script>
</head>
<body>
    
</body>
</html>

 

自个倒腾的解析XML格式字符串的类【具备一定的通用性】

标签:

原文地址:http://www.cnblogs.com/yuzhenghua/p/5533293.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!