這里是基于node的xmldom上擴展的工具,在使用appium的時候,常常需要用source功能來分析當前上下文,所以擴展了若干函數,用于分析。
這些代碼是基于node 6.9.x JavaScript ES6語法實現。(關于如何在node使用ES6的語法,請參考我的前文:js筆記四:node 6.9.x for gulp完整配置過程)
完成代碼如下:xml_utils.js
1 import { DOMParser } from "xmldom";
2
3 xml_utils = {};
4 //------------------------------------------------------------------------------
5 //解析XML字符串,并返回XML的dom對象
6 xml_utils.parseFromString = function ( xmlstring ) {
7 return new DOMParser().parseFromString( xmlstring );
8 }
9
10 //判斷是否有該屬性的值
11 xml_utils.hasNodeAttrib = function ( node, attrib, attrib_value, flag_index ) {
12 var nCnt = ( node && node.attributes ) ? node.attributes.length : 0;
13 for ( var i = 0; i < nCnt; i++ ) {
14 var attr = node.attributes[i];
15 //if (attr.name == attrib) {
16 // console.log("found attrib:" + attrib + "=" + attr.value + ", dest=" + attrib_value);
17 //}
18 if ( attr.name != attrib ) continue;
19 if ( flag_index === true ) {
20 if ( attr.value.indexOf( attrib_value ) >= 0 ) return true;
21 }
22 else {
23 if ( attr.value.trim() == attrib_value ) return true;
24 }
25 }
26 return false;
27 }
28
29 //取指定的屬性
30 xml_utils.getNodeAttrib = function ( node, attrib ) {
31 var nCnt = ( node && node.attributes ) ? node.attributes.length : 0;
32 for ( var i = 0; i < nCnt; i++ ) {
33 var attr = node.attributes[i];
34 if ( attr.name == attrib ) return attr;
35 }
36 return null;
37 }
38
39 //前一個節點
40 xml_utils.getNodePre = function ( node ) {
41 if ( node == null || node.parentNode == null ) return null;
42 var pNode = node.parentNode;
43 var nCnt = pNode.childNodes ? pNode.childNodes.length : 0;
44 var nIndex = -1;
45 for ( var i = 0; i < nCnt; i++ ) {
46 if ( pNode.childNodes.item( i ) === node ) {
47 nIndex = i;
48 break;
49 }
50 }
51 if ( nIndex > 0 ) {
52 var nNextIndex = nIndex - 1;
53 if ( nNextIndex < nCnt ) return pNode.childNodes.item( nNextIndex );
54 }
55 return null;
56 }
57
58
59 //下一個節點
60 xml_utils.getNodeNext = function ( node ) {
61 if ( node == null || node.parentNode == null ) return null;
62 var pNode = node.parentNode;
63 var nCnt = pNode.childNodes ? pNode.childNodes.length : 0;
64 var nIndex = -1;
65 for ( var i = 0; i < nCnt; i++ ) {
66 if ( pNode.childNodes.item( i ) === node ) {
67 nIndex = i;
68 break;
69 }
70 }
71 if ( nIndex >= 0 ) {
72 var nNextIndex = nIndex + 1;
73 if ( nNextIndex < nCnt ) return pNode.childNodes.item( nNextIndex );
74 }
75 return null;
76 }
77
78 //查找指定屬性的節點
79 xml_utils.findNodeByAttribName = function ( node, attrib, attrib_value, flag_index ) {
80 if ( this.hasNodeAttrib( node, attrib, attrib_value, flag_index ) ) return node;
81 //便利所有的子節點 使用深度遍歷的方式
82 var nChildCnt = node.childNodes ? node.childNodes.length : 0;
83 for ( var i = 0; i < nChildCnt; i++ ) {
84 var n = node.childNodes.item( i );
85 var nRet = this.findNodeByAttribName( n, attrib, attrib_value, flag_index );
86 if ( nRet ) {
87 return nRet;
88 }
89 }
90 return null;
91 }
92
93 //將節點數組中指定的屬性值,放到集合中
94 xml_utils.nodesAttribToSet = function (nodeArray, attribName) {
95 let retSet = new Set();
96 for(let n of nodeArray)
97 {
98 if(n.attrMap)
99 {
100 let v = n.attrMap.get(attribName);
101 if(v) retSet.add(v);
102 }
103 else
104 {
105 let nCnt = n.attributes ? n.attributes.length : 0;
106 for(let i = 0;i < nCnt;i++) {
107 let attr = n.attributes.item(i);
108 if(attr && attr.name.trim() == attribName)
109 {
110 retSet.add(attr.value.trim());
111 break;
112 }
113 }
114 }
115 }
116 return retSet;
117 }
118
119 //通過指定的函數,來確定要設的值
120 xml_utils.nodesAttribToSetEx = function (nodeArray, getFunction) {
121 if(typeof getFunction === "function") {
122 let retSet = new Set();
123 for(let n of nodeArray)
124 {
125 let [r,txt] = getFunction(n);
126 if(r) retSet.add(txt.trim());
127 }
128 return retSet;
129 }
130 else if(Array.isArray(getFunction)) { //如果傳入的是一個數組
131 return xml_utils.nodesAttribToSetEx(nodeArray, new Set(getFunction));
132 }
133 else if(getFunction instanceof Set) {
134 let s = getFunction;
135 let retSet = new Set();
136 for (let n of nodeArray) {
137 let nCnt = n.attributes ? n.attributes.length : 0;
138 for (let i = 0; i < nCnt; i++) {
139 let attr = n.attributes.item(i);
140 if (attr && s.has(attr.name.trim())) {
141 if (!attr.value) continue; //如果屬性值不存在
142 let v = attr.value.trim();
143 if (v.length == 0) continue; //如果為空串
144 retSet.add(v);
145 }
146 }
147 }
148 return retSet;
149 }
150 else return new Set();
151 //if(!(typeof getFunction ==="function")) return retSet;
152 }
153
154 //判斷是否有指定含有屬性的節點 這里會要求指定節點名稱,也就是class 其它需要的屬性,則要另外判斷
155 //node表示是當前的節點
156 //要查找的節點名稱和屬性列表 [{class:"xxx",y:"134"},{class:"yyyy"}]
157 //返回符合條件的節點數組
158 //這里的方法,類似于 getNodesByAttrib 但是這個方法在性能上做了優化,參數更加直觀
159 xml_utils.getNodesByClass = function(node, classNameList) {
160 let r = [];
161 if(!node) r;
162
163 let cmap = new Map(); //class和class條件列表
164
165 if(Array.isArray(classNameList)) {
166 for(let e of classNameList) { //將每個條件取出來
167 let classValue = e.class;
168 if(classValue)
169 {
170 let nCnt = 0;
171 for(let i in e) nCnt ++; //計算需要比較屬性的數量,并保存,用于后面比較
172 cmap.set(classValue, {c:e, count:nCnt});
173 }
174 }
175 }
176
177 function check(n) {
178 if(!n) return false; //如果節點為null,則返回false
179
180 let attrCnt = xml_utils.getAttributeCount(n);
181 if(attrCnt == 0) return false; //沒有沒有任何屬性
182
183 let e = cmap.get(n.nodeName); //取條件列表,如果沒有它的條件列表,則返回false
184 if(!e) return false;
185 let c = e.c; //條件列表
186
187 //構建屬性映射表
188 let foundCount = 0;
189 for(let i = 0; i < attrCnt; i++) {
190 let attr = n.attributes[i];
191 let cValue = c[attr.name];
192 if(cValue === undefined) continue; //如果這個不是條件之一,則下一個
193 if(cValue === null || cValue === attr.value) foundCount++;
194 else return false; //如果找到屬性存在,但是屬性值不相同,則表示不合條件
195 }
196 //計算條件的數量
197 return e.count == foundCount;
198 }
199 //編歷所有節點
200 function findNode(n, r) {
201 if(check(n)) r.push(n);
202 let childCnt = xml_utils.getChildNodeCount(n);
203 for(let i = 0; i < childCnt; i++) {
204 findNode(n.childNodes.item(i),r);
205 }
206 }
207 findNode(node, r);
208 return r;
209 }
210
211 //判斷是否有指定含有屬性的節點
212 //node表示是當前的節點
213 //attrib是一個二維數組鍵值對 如[["attrib1","value1"],["attrib2","value2"],["attrib3"]]
214 //返回符合條件的節點數組
215 xml_utils.getNodesByAttrib = function (node, attrib) {
216 let r = [];
217 if(!node) r;
218 //構造條件
219 let c = {};
220 c.map = new Map();
221 c.set = new Set();
222 if(Array.isArray(attrib)) {
223 for(let e of attrib) {
224 if(!Array.isArray(e)) continue;
225 if(e.length == 1) c.set.add(e[0]);
226 else if(e.length == 2) {
227 let mm = c.map.get(e[0]);
228 if(!mm) {
229 mm = new Set();
230 c.map.set(e[0],mm);
231 }
232 mm.add(e[1]);
233 }
234 }
235 }
236 //檢查有沒有符合屬性的節點
237 function check(n) {
238 let attrCnt = xml_utils.getAttributeCount(n);
239 //編歷所有屬性,判斷是不是符合要求
240 for(let i = 0; i < attrCnt; i++) {
241 let attr = n.attributes[i];
242 if(c.set.has(attr.name)) return true;
243 let o = c.map.get(attr.name);
244 if(o && o.has(attr.value.trim())) return true;
245 }
246 return false;
247 }
248 //編歷所有節點
249 function findNode(n, r) {
250 if(check(n)) r.push(n);
251 let childCnt = xml_utils.getChildNodeCount(n);
252 for(let i = 0; i < childCnt; i++) {
253 findNode(n.childNodes.item(i),r);
254 }
255 }
256 findNode(node, r);
257 return r;
258 }
259
260 //判斷指定的節點中,是含有指定的屬性列表
261 //attrib是一個二維數組鍵值對 如[["attrib1","value1"],["attrib2","value2"],["attrib3"]]
262 xml_utils.hasAttribInNode = function (node, attrib) {
263 if(!Array.isArray(attrib)) return false;
264 let nAttribCount = (node && node.attributes) ? node.attributes.length : 0;
265 if(nAttribCount <= 0) return false;
266 let mapAttr = new Map();
267 for(let i = 0;i < nAttribCount;i++) {
268 let e = node.attributes[i];
269 mapAttr.set(e.name.trim(), e.value.trim());
270 }
271 let bRet = true;
272 for(let i = 0;i < attrib.length;i++) {
273 let attr = attrib[i];
274 if(!Array.isArray(attr)) continue; //如果不是數組,則忽略
275 if(attr.length <= 0) continue; //如果數組為空,則忽略
276 if(attr.length == 1) //如果只有一個屬性,則判斷該屬性是否存在
277 {
278 if(!mapAttr.has(attr[0])) return false;
279 }
280 else {
281 if(!(mapAttr.get(attr[0]) === attr[1])) return false;
282 }
283 }
284 node.attrMap = mapAttr;
285 return bRet;
286 }
287 //將一個xml節點含屬性轉為字符 不含比子節點
288 xml_utils.nodeAttribToString = function (node) {
289 let strRet = "<" + (node.tagName || "") + " ";
290 let nAttribCount = (node && node.attributes) ? node.attributes.length : 0;
291 for(let i = 0;i < nAttribCount;i++) {
292 let e = node.attributes[i];
293 strRet = strRet + ' ' + e.name + '="' + e.value + '"';
294 }
295 strRet += " />"
296 return strRet;
297 }
298
299
300 //取找tagName的第一個節點
301 /**
302 * 默認是深度遍歷
303 * @param node 開始查找的節點
304 * @param targetTagName 節點的名稱
305 * @param flagBreadth 廣度遍歷的標志 @default = false
306 */
307 xml_utils.findNodeByTagName = function (node, targetTagName, flagBreadth = false) {
308 if(!node) return null;
309 if(node.tagName === targetTagName) {
310 console.log("找到節點1:", xml_utils.nodeAttribToString(node));
311 return node;
312 }
313 let childCnt = node.childNodes?node.childNodes.length:0;
314 if(flagBreadth){
315 for(let i = 0; i < childCnt; i++)
316 {
317 let childNode = node.childNodes.item(i);
318 if(childNode.tagName === targetTagName) {
319 console.log("找到節點2:", xml_utils.nodeAttribToString(childNode));
320 return childNode;
321 }
322 }
323 for(let i = 0; i < childCnt; i++)
324 {
325 let n = this.findNodeByTagName(node.childNodes.item(i), targetTagName, flagBreadth);
326 if(n != null) return n;
327 }
328 }
329 else {
330 for(let i = 0; i < childCnt; i++)
331 {
332 let n = this.findNodeByTagName(node.childNodes.item(i), targetTagName, flagBreadth);
333 if(n != null) return n;
334 }
335 }
336 return null;
337 }
338
339 //取節點的子節點的個數
340 xml_utils.getChildNodeCount = function(node) {
341 if(!node) return 0;
342 if(!node.childNodes) return 0;
343 return node.childNodes.length;
344 }
345 //取節點的屬性個數
346 xml_utils.getAttributeCount = function(node) {
347 if(!node) return 0;
348 if(!node.attributes) return 0;
349 return node.attributes.length;
350 }
351 //取指定下標的子節點
352 xml_utils.getChildNodeByIndex = function (node, index) {
353 if(!node) return null;
354 if(!node.childNodes) return null;
355 return node.childNodes.item(index);
356 }
357 //取指定下標的屬性
358 xml_utils.getAttributeByIndex = function(node, index) {
359 if(node) return null;
360 if(!node.attributes) return null;
361 return node.attributes[index];
362 }
363 //打印指定node XML
364 //這個是用于測試
365 xml_utils.dumpXmlNode = function (pre, node) {
366 pre = pre || "";
367 let tagName = (node && node.tagName)?node.tagName:"";
368 let nChildCnt = xml_utils.getChildNodeCount(node);
369 let nAttrCnt = xml_utils.getAttributeCount(node);
370 let s = `${pre}<${tagName}`;
371 for(let i = 0; i < nAttrCnt; i++) {
372 let attr = node.attributes[i];
373 s = s + ` ${attr.name}="${attr.value}"`;
374 }
375 if(nChildCnt > 0) {
376 s = s + ">";
377 console.log(s);
378 for(let i = 0; i < nChildCnt; i++)
379 {
380 xml_utils.dumpXmlNode(pre + " ", node.childNodes.item(i));
381 }
382 console.log(`${pre}</${tagName}>`);
383 } else {
384 s = s + "/>";
385 console.log(s);
386 }
387 }
388 export { xml_utils};
389 export default xml_utils;