ios - NSXMLParser XML解析

目前大部分的的app的请求数据的类型都为json数据,对于json数据我们借助MJExtension或者其它优秀的第三方解析类库进行解析,即可得到显示的数据内容。但有时候也会遇到用xml数据的情况,对于xml数据的解析,应该怎样操作呢?下面我写一个简单的demo来实现一下。

1、看一下要解析的数据


    
        t123456
        模拟试卷一
        1
        1
        3
        3
    
    
        
            听对话回答问题1/听对话回答问题1
            6
        
        
            听对话回答问题2/听对话回答问题2
            6
        
        

分析一下:info标签内容数据,可以看作是一个字典(或类) 。sectionList标签的内容,可以看作一个数组里包含着两个元素(字典或类)。分析清楚后就可以为解析xml数据时的提供一个方向了。

2、构建解析类

NSXMLParser 实现的是sax方法解析xml文件。下面科普一下saxdom的解析方法有什么优缺点?
dom实现的原理是把整个xml文档一次性读出,放在一个树型结构里。在需要的时候,查找特定节点,然后对节点进行读或写。
优点:实现简单,读写平衡;
缺点:比较占内存,因为他要把整个xml文档都读入内存,文件越大,缺点就越明显。
sax的实现方法和dom不同。它只在xml文档中查找特定条件的内容,并且只提取需要的内容。
优点:占用内存小,灵活。
缺点:编写实现上比较复杂。

(1)、构建一个解析工具类

ParserManager.h

@interface ParserManager : NSObject

/**
 初始化方法

 @param data 解析的xml数据
 @return ParserManager
 */
-(instancetype)initWithData:(NSData *)data;


/**
 解析完成时调用

 @param completion 完成时回调(demo中是把解析出来的数据返回了)
 */
-(void)finishedParser:(void(^)(NSArray *modeArray,NSDictionary *modeDic,NSError *error))completion;


@end

ParserManager.m
定义一个NSXMLParser类,以及一些必要的属性,属性的定义可以根据xml的数据结构来定义

@interface ParserManager ()
/** 定义一个解析类 */
@property(nonatomic,strong) NSXMLParser *parser;
/** 记录当前解析的节点 */
@property(nonatomic,strong) NSString *currentElementName;
/** 记录当前解析的类的属性 */
@property(nonatomic,strong) NSMutableDictionary *currentParserDic;
/** 记录当前解析的类的数组 */
@property(nonatomic,strong) NSMutableArray *currentParserArray;
/** 接收解析出来的dic数据 */
@property(nonatomic,strong) NSMutableDictionary *modeDic;
/** 接收解析出来的array数据 */
@property(nonatomic,strong) NSMutableArray *modeArray;
/** 解析完成的回调 */
@property(nonatomic,copy) void(^parserFinished)(NSArray *demoArray,NSDictionary *demoDic,NSError *error);
@end

初始化以及懒加载

-(instancetype)initWithData:(NSData *)data{
    
    if (self = [super init]) {
        self.parser = [[NSXMLParser alloc] initWithData:data];
        self.parser.delegate = self;
        //开始解析
        [self.parser parse];
    }
    return self;
}

-(NSMutableDictionary *)currentParserDic{
    if (!_currentParserDic) {
        _currentParserDic = [NSMutableDictionary dictionary];
    }
    return _currentParserDic;
}

-(NSMutableArray *)currentParserArray{
    if (!_currentParserArray) {
        _currentParserArray = [NSMutableArray array];
    }
    return _currentParserArray;
}
(2)、执行NSXMLParserDelegate代理方法

接下来的事情就简单了,只需在NSXMLParserDelegate代理方法中进行操作即可。
执行解析方法会调用时会调用:

-(void)parserDidStartDocument:(NSXMLParser *)parser{
//    NSLog(@"开始解析");
}

读取到节点时会执行,按照demo的xml数据解析会首先读取到的节点是[从上往下逐行逐字进行解析]。此外获取节点中的属性值也在此方法中操作,比如要获取节点的的属性paperpath的值。(demo中在此方法中对节点中清除了currentParserArraycurrentParserArray内的数据,目的是保证解析数据的准确性)。

//开始解析到某个节点是调用
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
    self.currentElementName = elementName;
    
    //当节点解析到info时开始准备一个字典接收解析出来的数据
    if ([elementName isEqualToString:@"info"]) {
        [self.currentParserArray removeAllObjects];
        [self.currentParserDic removeAllObjects];
    }
    //同理,当节点解析到sectionList时开始准备一个数组接收解析出来的数据
    if ([elementName isEqualToString:@"sectionList"]) {
        [self.currentParserArray removeAllObjects];
        [self.currentParserDic removeAllObjects];
    }
    //获取节点中的属性值
    if ([elementName isEqualToString:@"paperName"]) {
        NSLog(@"%@",attributeDict[@"paperpath"]);
    }
}

获取节点数据

//取值
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
    //didEndElement方法执行后,即节点解析结束后还会调用此方法,解析出来的string的值为"\n    ",并不是想要显示数据所以要做过滤操作
    if (self.currentElementName && [self isValidationString:string]) {
        [self.currentParserDic setValue:string forKey:self.currentElementName];
        [self.currentParserArray addObject:self.currentParserDic];
    }
}

节点解析结束。注意:self.currentElementName = nil;(按照demo的xml数据解析会首先读取到的节点是,[从上往下逐行逐字进行解析])

//结束解析某个节点时调用

-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
    //把数据取出
    if ([elementName isEqualToString:@"info"]) {
        self.modeDic = self.currentParserDic.copy;
    }
    if ([elementName isEqualToString:@"sectionList"]) {
        self.modeArray = self.currentParserArray.copy;
    } 
    self.currentElementName = nil;
}

结束整个xml文件解析时调用

//结束整个xml文件解析时调用
-(void)parserDidEndDocument:(NSXMLParser *)parser{
    //放在主线程中处理是为了解决self.parserFinished未被初始化的问题
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.parserFinished) {
            self.parserFinished(self.modeArray,self.modeDic,nil);
        }
    });
}

解析出错时调用的代理方法


-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
       dispatch_async(dispatch_get_main_queue(), ^{
        if (self.parserFinished) {
            self.parserFinished(nil,nil,parseError);
        }
    });
}
-(void)parser:(NSXMLParser *)parser validationErrorOccurred:(NSError *)validationError{
       dispatch_async(dispatch_get_main_queue(), ^{
        if (self.parserFinished) {
            self.parserFinished(nil,nil,validationError);
        }
    });
}
(3)、外调方法以及私有方法的实现
-(void)finishedParser:(void (^)(NSArray *, NSDictionary *,NSError*))completion{
    self.parserFinished = completion;
}

//过滤不必要的数据
-(BOOL)isValidationString:(NSString *)string{
    if ([string hasPrefix:@"\n"]){
        return NO;
    }else{
        return YES;
    }
}
(4)、控制器调用并查看效果
- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"xml"];
    NSData *data = [NSData dataWithContentsOfFile:filePath];
    ParserManager *parser = [[ParserManager alloc] initWithData:data];
    __weak typeof(self) weakSelf = self;
    [parser finishedParser:^(NSArray *modeArray, NSDictionary *modeDic, NSError *error) {
        if (!error) {
            weakSelf.contentText.text = [NSString stringWithFormat:@"%@\n%@",modeArray,modeDic];
        }else{
            NSLog(@"error:%@",error.localizedDescription);
        }
    }];
}
ios - NSXMLParser XML解析_第1张图片
demo打印信息.png

接下来就是字典转模型的步骤了,这应该都玩的挺6的了,就不多说了。

总结:1、要清楚xml数据的结构 2、要清楚NSXMLParser解析类代理方法的调用顺序。明白这两点就可以很快的把xml数据解析出来了。


都看到这里了,点个喜欢❤️不过分吧

你可能感兴趣的:(ios - NSXMLParser XML解析)