持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
本文以iPhone自带的计算器App为例,探究如何解析页面的XML,并获取控件的XPath~
计算器App页面、对应的XML字符串
// 描述iPhone计算器页面的xml
<?xml version="1.0" encoding="UTF-8"?>
<XCUIElementTypeApplication type="XCUIElementTypeApplication" name="计算器" label="计算器" enabled="true" visible="true" x="0" y="0" width="414" height="736">
<XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="true" x="0" y="0" width="414" height="736">
<XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="0" y="0" width="414" height="736">
<XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="3333" name="结果" label="结果" enabled="true" visible="true" x="0" y="20" width="414" height="219"/>
<XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="0" y="239" width="414" height="497">
<XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="0" y="239" width="414" height="497">
<XCUIElementTypeButton type="XCUIElementTypeButton" name="清除" label="清除" enabled="true" visible="true" x="16" y="239" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="正数负数" label="正数负数" enabled="true" visible="true" x="115" y="239" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="百分比" label="百分比" enabled="true" visible="true" x="214" y="239" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="除" label="除" enabled="true" visible="true" x="313" y="239" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="7" label="7" enabled="true" visible="true" x="16" y="338" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="8" label="8" enabled="true" visible="true" x="115" y="338" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="9" label="9" enabled="true" visible="true" x="214" y="338" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="乘" label="乘" enabled="true" visible="true" x="313" y="338" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="4" label="4" enabled="true" visible="true" x="16" y="437" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="5" label="5" enabled="true" visible="true" x="115" y="437" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="6" label="6" enabled="true" visible="true" x="214" y="437" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="减" label="减" enabled="true" visible="true" x="313" y="437" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="1" label="1" enabled="true" visible="true" x="16" y="536" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="2" label="2" enabled="true" visible="true" x="115" y="536" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="3" label="3" enabled="true" visible="true" x="214" y="536" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="加" label="加" enabled="true" visible="true" x="313" y="536" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="0" label="0" enabled="true" visible="true" x="16" y="635" width="184" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="小数点" label="小数点" enabled="true" visible="true" x="214" y="635" width="85" height="85"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="等于" label="等于" enabled="true" visible="true" x="313" y="635" width="85" height="85"/>
</XCUIElementTypeOther>
</XCUIElementTypeOther>
</XCUIElementTypeOther>
</XCUIElementTypeWindow>
<XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="false" x="0" y="0" width="414" height="736">
<XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="false" x="0" y="0" width="414" height="736">
<XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="false" x="0" y="736" width="414" height="226"/></XCUIElementTypeOther>
</XCUIElementTypeWindow>
<XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="true" x="0" y="0" width="414" height="736">
<XCUIElementTypeStatusBar type="XCUIElementTypeStatusBar" enabled="true" visible="true" x="0" y="0" width="414" height="20">
<XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="false" x="0" y="0" width="414" height="20"/>
<XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="0" y="0" width="414" height="20">
<XCUIElementTypeOther type="XCUIElementTypeOther" name="信号强度:4(共 4 格)" label="信号强度:4(共 4 格)" enabled="true" visible="true" x="6" y="0" width="16" height="20"/>
<XCUIElementTypeOther type="XCUIElementTypeOther" value="SSID" name="3 格(共 3 格无线局域网信号)" label="3 格(共 3 格无线局域网信号)" enabled="true" visible="true" x="25" y="0" width="14" height="20"/>
<XCUIElementTypeOther type="XCUIElementTypeOther" name="09:41" label="09:41" enabled="true" visible="true" x="192" y="0" width="34" height="20"/>
<XCUIElementTypeOther type="XCUIElementTypeOther" name="方向已锁定" label="方向已锁定" enabled="true" visible="true" x="306" y="0" width="12" height="20"/>
<XCUIElementTypeOther type="XCUIElementTypeOther" name="闹钟" label="闹钟" enabled="true" visible="true" x="324" y="0" width="10" height="20"/>
<XCUIElementTypeOther type="XCUIElementTypeOther" name="电池电量:100%, 使用交流电源" label="电池电量:100%, 使用交流电源" enabled="true" visible="true" x="340" y="0" width="69" height="20"/>
<XCUIElementTypeOther type="XCUIElementTypeOther" name="电池电量:100%" label="电池电量:100%" enabled="true" visible="false" x="340" y="0" width="31" height="20"/></XCUIElementTypeOther>
</XCUIElementTypeStatusBar>
</XCUIElementTypeWindow>
</XCUIElementTypeApplication>
封装好的API
支持以下功能:
- 获取当前页面全部控件的数组
- 查找指定的控件(例如计算器的某个按钮)
- 获取特定控件的值(例如计算器结果框的值)
- 执行相应的测试逻辑
//
// ViewController.m
// XML解析
//
// Created by YPF on 2018/8/16.
// Copyright © 2018年 原鹏飞. All rights reserved.
//
#import "ViewController.h"
@interface ViewController() <NSXMLParserDelegate>
@property (nonatomic, assign) NSInteger x;
@property (nonatomic, assign) NSInteger y;
@property (nonatomic, copy) NSString *xmlStr;
@property (nonatomic, copy) NSString *xpath;
@property (nonatomic, strong) NSMutableArray *pathArrM;
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *qualifiedElementArrM;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_xmlStr = @"<?xml version="1.0" encoding="UTF-8"?>\n<XCUIElementTypeApplication type="XCUIElementTypeApplication" name="计算器" label="计算器" enabled="true" visible="true" x="0" y="0" width="414" height="736">\n <XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="true" x="0" y="0" width="414" height="736">\n <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="0" y="0" width="414" height="736">\n <XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="77777" name="结果" label="结果" enabled="true" visible="true" x="0" y="20" width="414" height="219"/>\n <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="0" y="239" width="414" height="497">\n <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="0" y="239" width="414" height="497">\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="清除" label="清除" enabled="true" visible="true" x="16" y="239" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="正数负数" label="正数负数" enabled="true" visible="true" x="115" y="239" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="百分比" label="百分比" enabled="true" visible="true" x="214" y="239" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="除" label="除" enabled="true" visible="true" x="313" y="239" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="7" label="7" enabled="true" visible="true" x="16" y="338" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="8" label="8" enabled="true" visible="true" x="115" y="338" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="9" label="9" enabled="true" visible="true" x="214" y="338" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="乘" label="乘" enabled="true" visible="true" x="313" y="338" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="4" label="4" enabled="true" visible="true" x="16" y="437" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="5" label="5" enabled="true" visible="true" x="115" y="437" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="6" label="6" enabled="true" visible="true" x="214" y="437" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="减" label="减" enabled="true" visible="true" x="313" y="437" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="1" label="1" enabled="true" visible="true" x="16" y="536" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="2" label="2" enabled="true" visible="true" x="115" y="536" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="3" label="3" enabled="true" visible="true" x="214" y="536" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="加" label="加" enabled="true" visible="true" x="313" y="536" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="0" label="0" enabled="true" visible="true" x="16" y="635" width="184" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="小数点" label="小数点" enabled="true" visible="true" x="214" y="635" width="85" height="85"/>\n <XCUIElementTypeButton type="XCUIElementTypeButton" name="等于" label="等于" enabled="true" visible="true" x="313" y="635" width="85" height="85"/>\n </XCUIElementTypeOther>\n </XCUIElementTypeOther>\n </XCUIElementTypeOther>\n </XCUIElementTypeWindow>\n <XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="false" x="0" y="0" width="414" height="736">\n <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="false" x="0" y="0" width="414" height="736">\n <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="false" x="0" y="736" width="414" height="226"/>\n </XCUIElementTypeOther>\n </XCUIElementTypeWindow>\n <XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="true" x="0" y="0" width="414" height="736">\n <XCUIElementTypeStatusBar type="XCUIElementTypeStatusBar" enabled="true" visible="true" x="0" y="0" width="414" height="20">\n <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="false" x="0" y="0" width="414" height="20"/>\n <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" x="0" y="0" width="414" height="20">\n <XCUIElementTypeOther type="XCUIElementTypeOther" name="信号强度:4(共 4 格)" label="信号强度:4(共 4 格)" enabled="true" visible="true" x="6" y="0" width="16" height="20"/>\n <XCUIElementTypeOther type="XCUIElementTypeOther" value="SSID" name="3 格(共 3 格无线局域网信号)" label="3 格(共 3 格无线局域网信号)" enabled="true" visible="true" x="25" y="0" width="14" height="20"/>\n <XCUIElementTypeOther type="XCUIElementTypeOther" name="09:41" label="09:41" enabled="true" visible="true" x="192" y="0" width="34" height="20"/>\n <XCUIElementTypeOther type="XCUIElementTypeOther" name="方向已锁定" label="方向已锁定" enabled="true" visible="true" x="306" y="0" width="12" height="20"/>\n <XCUIElementTypeOther type="XCUIElementTypeOther" name="闹钟" label="闹钟" enabled="true" visible="true" x="324" y="0" width="10" height="20"/>\n <XCUIElementTypeOther type="XCUIElementTypeOther" name="电池电量:100%, 使用交流电源" label="电池电量:100%, 使用交流电源" enabled="true" visible="true" x="340" y="0" width="69" height="20"/>\n <XCUIElementTypeOther type="XCUIElementTypeOther" name="电池电量:100%" label="电池电量:100%" enabled="true" visible="false" x="340" y="0" width="31" height="20"/>\n </XCUIElementTypeOther>\n </XCUIElementTypeStatusBar>\n </XCUIElementTypeWindow>\n</XCUIElementTypeApplication>\n";
_pathArrM = [[NSMutableArray alloc] init];
}
/**
懒加载筛选出的控件数组
@return 筛选结果数组
*/
- (NSMutableArray *)qualifiedElementArrM {
if (!_qualifiedElementArrM) {
_qualifiedElementArrM = [[NSMutableArray alloc] init];
}
return _qualifiedElementArrM;
}
- (IBAction)clickStartBtn:(NSButton *)sender {
// 假设这里判断点(100, 350)对应的是哪个控件
_x = 100;
_y = 350;
[self useXMLParserWithData:[self.xmlStr dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(@"筛选结果:%@", self.qualifiedElementArrM);
NSLog(@"elementXpath = %@", [self.qualifiedElementArrM.firstObject valueForKey:@"elementXpath"]);
}
- (void)useXMLParserWithData:(NSData *)data {
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:data];
[xmlParser setDelegate:self];
[xmlParser parse];
}
#pragma mark - NSXMLParserDelegate
/**
开始解析文档
@param parser 解析器
*/
- (void)parserDidStartDocument:(NSXMLParser *)parser {
NSLog(@"开始解析XML文件!");
}
/**
开始标签
@param parser 解析器
@param elementName 元素名称
@param namespaceURI 命名空间
@param qName 限定名
@param attributeDict 属性字典
*/
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict {
[self.pathArrM addObject:elementName];
if ([elementName isEqualToString:@"XCUIElementTypeButton"]) {
NSLog(@"找到一个按钮 qualifiedName = %@, attributes = %@", qName, attributeDict);
// 判断x坐标
if ([attributeDict valueForKey:@"x"].integerValue <= self.x && self.x <= [attributeDict valueForKey:@"x"].integerValue + [attributeDict valueForKey:@"width"].integerValue) {
// 判断y
if ([attributeDict valueForKey:@"y"].integerValue <= self.y && self.y <= [attributeDict valueForKey:@"y"].integerValue + [attributeDict valueForKey:@"height"].integerValue) {
NSString *elementXpath = [NSString stringWithFormat:@"//%@[@name='%@']", [self.pathArrM componentsJoinedByString:@"/"], attributeDict[@"name"]];
NSDictionary *elementDict = @{@"elementXpath":elementXpath,
@"attributeDict":attributeDict
};
// 加入筛选合格的控件数组
[self.qualifiedElementArrM addObject:elementDict];
}
}
}
if ([elementName isEqualToString:@"XCUIElementTypeStaticText"]) {
NSLog(@"找到一个文本框 qualifiedName = %@, attributes = %@", qName, attributeDict);
// 判断x坐标
if ([attributeDict valueForKey:@"x"].integerValue <= self.x && self.x <= [attributeDict valueForKey:@"x"].integerValue + [attributeDict valueForKey:@"width"].integerValue) {
// 判断y
if ([attributeDict valueForKey:@"y"].integerValue <= self.y && self.y <= [attributeDict valueForKey:@"y"].integerValue + [attributeDict valueForKey:@"height"].integerValue) {
NSString *elementXpath = [NSString stringWithFormat:@"//%@[@name='%@']", [self.pathArrM componentsJoinedByString:@"/"], attributeDict[@"name"]];
NSDictionary *elementDict = @{@"elementXpath":elementXpath,
@"attributeDict":attributeDict
};
// 加入筛选合格的控件数组
[self.qualifiedElementArrM addObject:elementDict];
}
}
}
}
/**
找到指定的字符串
@param parser 解析器
@param string 指定要查找的字符串
*/
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
}
/**
结束标签
@param parser 解析器
@param elementName 元素名称
@param namespaceURI 命名空间
@param qName 限定名
*/
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
NSLog(@"XML解析完成一个元素:elementName = %@,qualifiedName = %@🐶", elementName, qName);
if ([elementName isEqualToString:self.pathArrM.lastObject]) {
[self.pathArrM removeLastObject];
}
}
/**
文档解析结束
@param parser 解析器
*/
- (void)parserDidEndDocument:(NSXMLParser *)parser {
}
#pragma mark - 自带方法
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
// Update the view, if already loaded.
}
@end