标签:
在公司写了很多解析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>
标签:
原文地址:http://www.cnblogs.com/yuzhenghua/p/5533293.html