这里是基于node的xmldom上扩展的工具,在使用appium的时候,常常需要用source功能来分析当前上下文,所以扩展了若干函数,用于分析。
这些代码是基于node 6.9.x javascript ES6语法实现。(关于如何在node使用ES6的语法,请参考我的前文:js笔记四:node 6.9.x for gulp完整配置过程)
完成代码如下:xml_utils.js
import { DOMParser } from "xmldom"; xml_utils = {}; //------------------------------------------------------------------------------ //解析XML字符串,并返回XML的dom对象 xml_utils.parseFromString = function ( xmlstring ) { return new DOMParser().parseFromString( xmlstring ); } //判断是否有该属性的值 xml_utils.hasNodeAttrib = function ( node,attrib,attrib_value,flag_index ) { var nCnt = ( node && node.attributes ) ? node.attributes.length : 0; for ( var i = 0; i < nCnt; i++ ) { var attr = node.attributes[i]; //if (attr.name == attrib) { // console.log("found attrib:" + attrib + "=" + attr.value + ",dest=" + attrib_value); //} if ( attr.name != attrib ) continue; if ( flag_index === true ) { if ( attr.value.indexOf( attrib_value ) >= 0 ) return true; } else { if ( attr.value.trim() == attrib_value ) return true; } } return false; } //取指定的属性 xml_utils.getNodeAttrib = function ( node,attrib ) { var nCnt = ( node && node.attributes ) ? node.attributes.length : 0; for ( var i = 0; i < nCnt; i++ ) { var attr = node.attributes[i]; if ( attr.name == attrib ) return attr; } return null; } //前一个节点 xml_utils.getNodePre = function ( node ) { if ( node == null || node.parentNode == null ) return null; var pNode = node.parentNode; var nCnt = pNode.childNodes ? pNode.childNodes.length : 0; var nIndex = -1; for ( var i = 0; i < nCnt; i++ ) { if ( pNode.childNodes.item( i ) === node ) { nIndex = i; break; } } if ( nIndex > 0 ) { var nNextIndex = nIndex - 1; if ( nNextIndex < nCnt ) return pNode.childNodes.item( nNextIndex ); } return null; } //下一个节点 xml_utils.getNodeNext = function ( node ) { if ( node == null || node.parentNode == null ) return null; var pNode = node.parentNode; var nCnt = pNode.childNodes ? pNode.childNodes.length : 0; var nIndex = -1; for ( var i = 0; i < nCnt; i++ ) { if ( pNode.childNodes.item( i ) === node ) { nIndex = i; break; } } if ( nIndex >= 0 ) { var nNextIndex = nIndex + 1; if ( nNextIndex < nCnt ) return pNode.childNodes.item( nNextIndex ); } return null; } //查找指定属性的节点 xml_utils.findNodeByAttribName = function ( node,flag_index ) { if ( this.hasNodeAttrib( node,flag_index ) ) return node; //便利所有的子节点 使用深度遍历的方式 var nChildCnt = node.childNodes ? node.childNodes.length : 0; for ( var i = 0; i < nChildCnt; i++ ) { var n = node.childNodes.item( i ); var nRet = this.findNodeByAttribName( n,flag_index ); if ( nRet ) { return nRet; } } return null; } //将节点数组中指定的属性值,放到集合中 xml_utils.nodesAttribToSet = function (nodeArray,attribName) { let retSet = new Set(); for(let n of nodeArray) { if(n.attrMap) { let v = n.attrMap.get(attribName); if(v) retSet.add(v); } else { let nCnt = n.attributes ? n.attributes.length : 0; for(let i = 0;i < nCnt;i++) { let attr = n.attributes.item(i); if(attr && attr.name.trim() == attribName) { retSet.add(attr.value.trim()); break; } } } } return retSet; } //通过指定的函数,来确定要设的值 xml_utils.nodesAttribToSetEx = function (nodeArray,getFunction) { if(typeof getFunction === "function") { let retSet = new Set(); for(let n of nodeArray) { let [r,txt] = getFunction(n); if(r) retSet.add(txt.trim()); } return retSet; } else if(Array.isArray(getFunction)) { //如果传入的是一个数组 return xml_utils.nodesAttribToSetEx(nodeArray,new Set(getFunction)); } else if(getFunction instanceof Set) { let s = getFunction; let retSet = new Set(); for (let n of nodeArray) { let nCnt = n.attributes ? n.attributes.length : 0; for (let i = 0; i < nCnt; i++) { let attr = n.attributes.item(i); if (attr && s.has(attr.name.trim())) { if (!attr.value) continue; //如果属性值不存在 let v = attr.value.trim(); if (v.length == 0) continue; //如果为空串 retSet.add(v); } } } return retSet; } else return new Set(); //if(!(typeof getFunction ==="function")) return retSet; } //判断是否有指定含有属性的节点 这里会要求指定节点名称,也就是class 其它需要的属性,则要另外判断 //node表示是当前的节点 //要查找的节点名称和属性列表 [{class:"xxx",y:"134"},{class:"yyyy"}] //返回符合条件的节点数组 //这里的方法,类似于 getNodesByAttrib 但是这个方法在性能上做了优化,参数更加直观 xml_utils.getNodesByClass = function(node,classNameList) { let r = []; if(!node) r; let cmap = new Map(); //class和class条件列表 if(Array.isArray(classNameList)) { for(let e of classNameList) { //将每个条件取出来 let classValue = e.class; if(classValue) { let nCnt = 0; for(let i in e) nCnt ++; //计算需要比较属性的数量,并保存,用于后面比较 cmap.set(classValue,{c:e,count:nCnt}); } } } function check(n) { if(!n) return false; //如果节点为null,则返回false let attrCnt = xml_utils.getAttributeCount(n); if(attrCnt == 0) return false; //没有没有任何属性 let e = cmap.get(n.nodeName); //取条件列表,如果没有它的条件列表,则返回false if(!e) return false; let c = e.c; //条件列表 //构建属性映射表 let foundCount = 0; for(let i = 0; i < attrCnt; i++) { let attr = n.attributes[i]; let cValue = c[attr.name]; if(cValue === undefined) continue; //如果这个不是条件之一,则下一个 if(cValue === null || cValue === attr.value) foundCount++; else return false; //如果找到属性存在,但是属性值不相同,则表示不合条件 } //计算条件的数量 return e.count == foundCount; } //编历所有节点 function findNode(n,r) { if(check(n)) r.push(n); let childCnt = xml_utils.getChildNodeCount(n); for(let i = 0; i < childCnt; i++) { findNode(n.childNodes.item(i),r); } } findNode(node,r); return r; } //判断是否有指定含有属性的节点 //node表示是当前的节点 //attrib是一个二维数组键值对 如[["attrib1","value1"],["attrib2","value2"],["attrib3"]] //返回符合条件的节点数组 xml_utils.getNodesByAttrib = function (node,attrib) { let r = []; if(!node) r; //构造条件 let c = {}; c.map = new Map(); c.set = new Set(); if(Array.isArray(attrib)) { for(let e of attrib) { if(!Array.isArray(e)) continue; if(e.length == 1) c.set.add(e[0]); else if(e.length == 2) { let mm = c.map.get(e[0]); if(!mm) { mm = new Set(); c.map.set(e[0],mm); } mm.add(e[1]); } } } //检查有没有符合属性的节点 function check(n) { let attrCnt = xml_utils.getAttributeCount(n); //编历所有属性,判断是不是符合要求 for(let i = 0; i < attrCnt; i++) { let attr = n.attributes[i]; if(c.set.has(attr.name)) return true; let o = c.map.get(attr.name); if(o && o.has(attr.value.trim())) return true; } return false; } //编历所有节点 function findNode(n,r); return r; } //判断指定的节点中,是含有指定的属性列表 //attrib是一个二维数组键值对 如[["attrib1",["attrib3"]] xml_utils.hasAttribInNode = function (node,attrib) { if(!Array.isArray(attrib)) return false; let nAttribCount = (node && node.attributes) ? node.attributes.length : 0; if(nAttribCount <= 0) return false; let mapAttr = new Map(); for(let i = 0;i < nAttribCount;i++) { let e = node.attributes[i]; mapAttr.set(e.name.trim(),e.value.trim()); } let bRet = true; for(let i = 0;i < attrib.length;i++) { let attr = attrib[i]; if(!Array.isArray(attr)) continue; //如果不是数组,则忽略 if(attr.length <= 0) continue; //如果数组为空,则忽略 if(attr.length == 1) //如果只有一个属性,则判断该属性是否存在 { if(!mapAttr.has(attr[0])) return false; } else { if(!(mapAttr.get(attr[0]) === attr[1])) return false; } } node.attrMap = mapAttr; return bRet; } //将一个xml节点含属性转为字符 不含比子节点 xml_utils.nodeAttribToString = function (node) { let strRet = "<" + (node.tagName || "") + " "; let nAttribCount = (node && node.attributes) ? node.attributes.length : 0; for(let i = 0;i < nAttribCount;i++) { let e = node.attributes[i]; strRet = strRet + ' ' + e.name + '="' + e.value + '"'; } strRet += " />" return strRet; } //取找tagName的第一个节点 /** * 默认是深度遍历 * @param node 开始查找的节点 * @param targetTagName 节点的名称 * @param flagBreadth 广度遍历的标志 @default = false */ xml_utils.findNodeByTagName = function (node,targetTagName,flagBreadth = false) { if(!node) return null; if(node.tagName === targetTagName) { console.log("找到节点1:",xml_utils.nodeAttribToString(node)); return node; } let childCnt = node.childNodes?node.childNodes.length:0; if(flagBreadth){ for(let i = 0; i < childCnt; i++) { let childNode = node.childNodes.item(i); if(childNode.tagName === targetTagName) { console.log("找到节点2:",xml_utils.nodeAttribToString(childNode)); return childNode; } } for(let i = 0; i < childCnt; i++) { let n = this.findNodeByTagName(node.childNodes.item(i),flagBreadth); if(n != null) return n; } } else { for(let i = 0; i < childCnt; i++) { let n = this.findNodeByTagName(node.childNodes.item(i),flagBreadth); if(n != null) return n; } } return null; } //取节点的子节点的个数 xml_utils.getChildNodeCount = function(node) { if(!node) return 0; if(!node.childNodes) return 0; return node.childNodes.length; } //取节点的属性个数 xml_utils.getAttributeCount = function(node) { if(!node) return 0; if(!node.attributes) return 0; return node.attributes.length; } //取指定下标的子节点 xml_utils.getChildNodeByIndex = function (node,index) { if(!node) return null; if(!node.childNodes) return null; return node.childNodes.item(index); } //取指定下标的属性 xml_utils.getAttributeByIndex = function(node,index) { if(node) return null; if(!node.attributes) return null; return node.attributes[index]; } //打印指定node XML //这个是用于测试 xml_utils.dumpXmlNode = function (pre,node) { pre = pre || ""; let tagName = (node && node.tagName)?node.tagName:""; let nChildCnt = xml_utils.getChildNodeCount(node); let nAttrCnt = xml_utils.getAttributeCount(node); let s = `${pre}<${tagName}`; for(let i = 0; i < nAttrCnt; i++) { let attr = node.attributes[i]; s = s + ` ${attr.name}="${attr.value}"`; } if(nChildCnt > 0) { s = s + ">"; console.log(s); for(let i = 0; i < nChildCnt; i++) { xml_utils.dumpXmlNode(pre + " ",node.childNodes.item(i)); } console.log(`${pre}</${tagName}>`); } else { s = s + "/>"; console.log(s); } } export { xml_utils}; export default xml_utils;原文链接:https://www.f2er.com/xml/294424.html