数据解析之XML解析详解(苹果自带XML解析)——并将数据转成Mode
什么是XML? 全称是Extensible Markup Language,可译为可扩展性标记语言。 跟JSON一样,也是一种用于交互的数据格式。 一般也叫做XML文档。 XML举例 <videos> <video name="小黄人第01部" length="30" /> <video name="小黄人第02部" length="19" /> <video name="小黄人第03部" length="33" /> </videos> XML语法 一个常见的XML文档一般由以下部分组成 文档声明 元素(Element) 属性(Attribute)
在XML文档的最前面,必须编写一个文档声明,用来声明XML文档的类型。 最简单的声明 <?xml version=“1.0”> 用encoding属性说明文档的编码 <?xml version=“1.0” encoding = “UTF-8”> XML语法—元素(ELement) 一个元素包括了开始标签和结束标签 拥有元素的内容:<video>小黄人</video> 没有元素的内容:<video></video> 没有内容的简写:<video/> 一个元素可以嵌套若干个子元素(不能出现交叉嵌套) <videos> <video> <name>小黄人</name> <length>30</length> </video> </videos> 交叉嵌套举例如下: <videos> <video> <name>小黄人</name> <length>30</length> </videos> </video> 规范的XML文档最多只有一个根元素,其他元素都是根元素的子孙元素。 XML语法——元素注意: XML中所有空格和换行,(空格和换行都会当做字符处理)都会当做具体内容处理。 下面两个元素的内容是不一样的。 第一个 <video>小黄人</video> 第二个 <video> 小黄人 </video> XML语法——属性(Attribute) 一个元素可以拥有很多属性。 <video name=“小黄人第01部”length=“30”> video元素拥有name和length两个属性。 属性值必须用双引号或单引号括住。 实际上,属性表示的信息也可以用子元素来表示,比如: <video> <name>小黄人第01部</name> <length>30</length> </video> 注意:一个元素相当于一个Model,一个元素属性相当于Model中的变量。每一个子元素相当于一个Model成员对象。
XML解析: 要想从XML中提取有用的信息,必须学会解析XML 提取name元素里面的内容 <name>小黄人第01部</name> 提取video元素中name和length属性的值 <video name=“小黄人第01部”length=“30”>
XML解析主要有两种 DOM:一次性将整个XML文档加载进内存,比较适合解析小文件。 SAX:从根元素开始,按顺序一个一个元素往下解析(类似于递归),比较适合解析大文件。 苹果自带的XML解析方法以及步骤如下: 第一步:创建解析器。 首先在获得网络数据后,实例化一个XML的解析器进行实现XML的解析。如下所示: NSXMLParser *parse = [[NSXMLParser alloc] initWithData: data]; 第二步:设置代理。并将当前控制器遵守NSXMLParserDelegate协议 parse.delegate = self; 第三步:开始解析。[parse parse]; 第四步:实现NSXMLParserDelegate里的五个必不可少的代理方法,如下所示: (1)打开文档开始准备解析。代理方法如下所示: - (void)parserDidStartDocument:(NSXMLParser *)parser (2)开始结点。代理方法如下所示: - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict (3)发现结点内容。代理方法如下所示: - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string (4)结束结点。代理方法如下所示: - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName (5)结束文档。代理方法如下所示: - (void)parserDidEndDocument:(NSXMLParser *)parser 注意:因为是递归执行,所以除了第一个打开文档和最后一个结束文档外,其他的都是循环执行的。 解析步骤如下: 步骤一、新建一个全局的可变容器,用来存放XML文档转化后的Model。 步骤二、新建一个全局的可变字符串变量,用来存放拼接后的字符串。 步骤三、根据解析内容新建对应的Model模型,并在当前控制器中新建对应的全局Model属性。 步骤四、在代理方法中对数据进行处理。处理大概包括以下几个过程: 4.1、在打开文档开始解析的代理方法(1)中初始化全局容器,即清空全局容器的内容。(为了防止多次解析时重复添加数据)。 4.2、在开始结点的代理方法中对各个结点元素的名称进行判断,并创建对应的Model对象以及设置Model对应的attributeDict字典内的属性。另外还需要把全局的可变字符串置为空(因为开始解析新的结点)。 4.3、在发现节点内容的代理方法(3)中,需要进行全局可变字符串的拼接操作。 4.4、在结束结点的代理方法(4)中需要对节点名称进行判断分别执行不同的操作(添加对应Model到全局可变容器或者利用KVC对Model的子元素进行赋值)。 4.5、在结束文档的代理方法中可以在主线程更新UI。 思路如下图:
新建工程,步骤如下: 3
4
5
6
7
8
新建模型类如下: Video.h
#import <Foundation/Foundation.h> @interface Video : NSObject @property (nonatomic,copy) NSNumber *videoId; @property (nonatomic,copy) NSString *name; @property (nonatomic,copy) NSNumber *length; @property (nonatomic,copy) NSString *videoURL; @property (nonatomic,copy) NSString *imageURL; @property (nonatomic,copy) NSString *desc; @property (nonatomic,copy) NSString *teacher; @property (nonatomic,readonly) NSString *time; - (instancetype)initWithDict:(NSDictionary *)dict; + (instancetype)videoWithDict:(NSDictionary *)dict; @end Video.m
#import "Video.h" @implementation Video - (instancetype)initWithDict:(NSDictionary *)dict { self = [super init]; if (self) { [self setValuesForKeysWithDictionary:dict]; } return self; } + (instancetype)videoWithDict:(NSDictionary *)dict { return [[self alloc] initWithDict:dict]; } - (NSString *)time { int len = self.length.intValue; return [NSString stringWithFormat:@"%02d:%02d:%02d",len / 3600,(len % 3600) / 60,(len % 60)]; } - (NSString *)description { return [NSString stringWithFormat:@"<%@ : %p> { videoId : %@,name : %@,length : %@,videoURL : %@,imageURL : %@,desc : %@,teacher : %@}",[self class],self,self.videoId,self.name,self.length,self.videoURL,self.imageURL,self.desc,self.teacher]; } @endviewController.m
// // ViewController.m // 07-xml解析 // // Created by apple on 15/1/19. // Copyright (c) 2015年 apple. All rights reserved. // #import "ViewController.h" #import "Video.h" @interface ViewController ()<NSXMLParserDelegate> /**整个xml的数据容器*/ @property(nonatomic,strong)NSMutableArray *videos; /**第3步拼接字符串*/ @property(nonatomic,strong)NSMutableString *elementString; /**当前的模型*/ @property(nonatomic,strong) Video *currentVideo; /**表格的所有数据*/ @property (nonatomic,strong) NSMutableArray *dataList; @end @implementation ViewController - (void)setDataList:(NSMutableArray *)dataList { _dataList = dataList; // 只要重新给表格的数据容器赋值,就刷新 [self.tableView reloadData]; // 隐藏刷新控件 [self.refreshControl endRefreshing]; } - (NSMutableArray *)videos { if (_videos == nil) { _videos = [NSMutableArray array]; } return _videos; } - (NSMutableString *)elementString { if (_elementString == nil) { _elementString = [NSMutableString string]; } return _elementString; } - (void)viewDidLoad { [super viewDidLoad]; [self loadData]; } #pragma mark - 加载数据 - (IBAction)loadData { // 1. url NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos.xml"]; // 2. 请求 NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:1 timeoutInterval:2.0f]; // 3. 连接 [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response,NSData *data,NSError *connectionError) { // xml的解析 // 1. 实例化一个xml的解析器--通过代理来实现xml的解析 NSXMLParser *parse = [[NSXMLParser alloc] initWithData:data]; // 2. 设置代理 parse.delegate = self; // 3. 解析器开始解析 [parse parse]; }]; } #pragma mark - 代理方法 //1.打开文档,准备开始解析 - (void)parserDidStartDocument:(NSXMLParser *)parser { NSLog(@"1. 打开文档,准备开始解析"); // 初始化数组容器,清空容器,便于多次加载数据 [self.videos removeAllObjects]; } //2.开始节点 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { NSLog(@"2.开始节点%@---%@",elementName,attributeDict); // 如果开始节点的名称 是video,就创建一个对象 if ([elementName isEqualToString:@"video"]) { self.currentVideo = [[Video alloc] init]; // 设置videoID self.currentVideo.videoId = attributeDict[@"videoId"]; } // 清空字符串的内容,因为马上要进入第3 个方法,要开始拼接当前的节点的内容 [self.elementString setString:@""]; } //3. 发现节点里面内容 - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { NSLog(@"3. 发现节点里面内容--->%@",string); // 开始拼接 [self.elementString appendString:string]; } //4. 结束节点 - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { NSLog(@"4. 结束节点 %@",elementName); if ([elementName isEqualToString:@"video"]) { // 如果结束的节点是video, 需要把这个对象添加到数组 [self.videos addObject:self.currentVideo]; }else if (![elementName isEqualToString:@"videos"]){ [self.currentVideo setValue:self.elementString forKeyPath:elementName]; } // else if ([elementName isEqualToString:@"name"]) {// 如果结束节点,就可以把第二步创建的对象,设置name/length属性 // // 设置name属性 // // 1. 直接赋值 //// self.currentVideo.name = self.elementString; // // 2. KVC赋值 //// [self.currentVideo setValue:self.elementString forKeyPath:@"name"]; // // 3. 改造 // [self.currentVideo setValue:self.elementString forKeyPath:elementName]; // }else if ([elementName isEqualToString:@"length"]){ // // 设置length属性 //// self.currentVideo.length = @([self.elementString integerValue]); // // [self.currentVideo setValue:self.elementString forKeyPath:elementName]; // }else if ([elementName isEqualToString:@"videoURL"]){ // // 设置videoURL属性 //// self.currentVideo.videoURL = self.elementString; // [self.currentVideo setValue:self.elementString forKeyPath:elementName]; // }else if ([elementName isEqualToString:@"desc"]){ // // 设置desc属性 //// self.currentVideo.desc = self.elementString; // [self.currentVideo setValue:self.elementString forKeyPath:elementName]; // }else if ([elementName isEqualToString:@"teacher"]){ // // 设置teacher属性 //// self.currentVideo.teacher = self.elementString; // [self.currentVideo setValue:self.elementString forKeyPath:elementName]; // } // cocoa 的大招 KVC } //5 结束文档 - (void)parserDidEndDocument:(NSXMLParser *)parser { NSLog(@"5 结束文档"); // xml真正解析结束,可以更新UI,在主线程 dispatch_async(dispatch_get_main_queue(),^{ self.dataList = self.videos; }); } #pragma tabel数据源方法 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.videos.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; Video *video = self.dataList[indexPath.row]; cell.textLabel.text = video.name; return cell; } @end本地服务器显示如下:
手机显示如下:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |