1.XML简介
- XML,全称是Extensible Markup Language,译作“可扩展标记语言”
- 跟JSON一样,也是常用的一种用于交互的数据格式
- 一般也叫XML文档(XML Document)
- XML举例
<videos>
<video name="小黄人 第01部" length="30" />
<video name="小黄人 第02部" length="19" />
<video name="小黄人 第03部" length="33" />
</videos>
2.XML语法概述
- 一个常见的XML文档一般由以下部分组成
- 文档声明
- 元素(Element)
- 属性(Attribute)
- XML语法 – 文档声明
- 在XML文档的最前面,必须编写一个文档声明,用来声明XML文档的类型
//最简单的声明,声明版本信息
<?xml version="1.0" ?>
//用encoding属性说明文档的字符编码信息
<?xml version="1.0" encoding="UTF-8" ?>
- XML语法 – 元素(Element)
- 一个元素包括了开始标签和结束标签
- 拥有内容的元素:小黄人
- 没有内容的元素:
- 没有内容的元素简写:
- 一个元素可以嵌套若干个子元素(不能出现交叉嵌套)
- 规范的XML文档最多只有1个根元素,其他元素都是根元素的子孙元素
- 一个元素包括了开始标签和结束标签
<videos>
<video>
<name>小黄人 第01部</name>
<length>30</length>
</video>
</videos>
- XML语法 –元素的注意
- XML中的所有空格和换行,都会当做具体内容处理
- 下面两个元素的内容是不一样的
- 第1个
<video>小黄人</video>
- 第2个
<video>
小黄人
</video>
- 第1个
- XML语法 – 属性(Attribute)
- 一个元素可以拥有多个属性
- video元素拥有name和length两个属性
- 属性值必须用 双引号”” 或者 单引号” 括住
<video name="小黄人 第01部" length="30" />
//实际上,属性表示的信息也可以用子元素来表示,比如
<video>
<name>小黄人 第01部</name>
<length>30</length>
</video>
3.XML解析
要想从XML中提取有用的信息,必须得学会解析XML
- 提取name元素里面的内容
<name>小黄人 第01部</name>
- 提取video元素中name和length属性的值
<video name="小黄人 第01部" length="30" />
- 提取name元素里面的内容
XML的解析方式有2种
- DOM:一次性将整个XML文档加载进内存,比较适合解析小文件
- SAX:从根元素开始,按顺序一个元素一个元素往下解析,比较适合解析大文件
在iOS中,解析XML的手段有很多
- 苹果原生NSXMLParser:SAX方式解析,使用简单
- 第三方框架libxml2:纯C语言,默认包含在iOS SDK中,同时支持DOM和SAX方式解析
- GDataXML:DOM方式解析,由Google开发,基于libxml2的封装
XML解析方式选择
- 大文件:NSXMLParser
- 小文件:GDataXML、NSXMLParser
4.SAX解析-NSXMLParser
- NSXMLParser
- 1.传入XML数据,创建解析器
- 2.设置代理,监听解析过程
- 3.开始解析
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
- NSXMLParser采取的是SAX方式解析,特点是事件驱动,下面情况都会通知代理
- 当扫描到文档(Document)的开始与结束
- 当扫描到元素(Element)的开始与结束
- 常见代理方法(NSXMLParserDelegate)
//当扫描到文档的开始时调用(开始解析)
- (void)parserDidStartDocument:(NSXMLParser *)parser
//当扫描到文档的结束时调用(解析完毕)
- (void)parserDidEndDocument:(NSXMLParser *)parser
//当扫描到元素的开始时调用(attributeDict存放着元素的属性)
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
// 当扫描到元素的结束时调用
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
- 解析实例
#import "ViewController.h"
#import "NSString+SandboxPath.h"
#import <SDWebImage/UIImageView+WebCache.h>
#import <MediaPlayer/MPMoviePlayerViewController.h>
@interface ViewController ()<NSXMLParserDelegate>
@property (nonatomic, strong) NSMutableArray *videos;// 视频信息
@end
@implementation ViewController
#pragma mark - 懒加载
-(NSMutableArray *)videos{
if (!_videos) {
_videos = [NSMutableArray array];
}
return _videos;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.rowHeight = 150;
// 1.获取网络数据
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/video?type=XML"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// 2.解析XML数据
// 2.1创建解析器
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
// 2.2设置代理
parser.delegate = self;
// 2.3开始解析
[parser parse];
}];
}
#pragma mark - NSXMLParserDelegate
// 只要开始解析XML文档就会调用
-(void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(@"%s",__func__);
}
// 只要解析完毕XML文档就会调用
-(void)parserDidEndDocument:(NSXMLParser *)parser{
NSLog(@"%s",__func__);
// 刷新UI
[self.tableView reloadData];
}
// 当扫描到元素的开始时调用
/* 参数解释:
elementName:元素名称
attributeDict:元素中的属性
namespaceURI和qualifiedName是XML DOM的属性,基本用不上
*/
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
NSLog(@"%s",__func__);
if ([elementName isEqualToString:@"videos"]) return;
[self.videos addObject:attributeDict];
NSLog(@"%@",attributeDict);
}
// 当扫描到元素的结束时调用
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
NSLog(@"%s",__func__);
}
// 解析发生错误时调用
-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
NSLog(@"%s",__func__);
}
#pragma mark - UITableViewDataSource
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.videos.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// 1.创建cell
static NSString *identifier = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
// 2.设置cell的数据
NSDictionary *dict = self.videos[indexPath.row];
cell.textLabel.text = dict[@"name"];
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@",dict[@"length"]];
NSString *urlStr =[NSString stringWithFormat:@"http://120.25.226.186:32812/%@", dict[@"image"]];
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
[cell.imageView sd_setImageWithURL:url placeholderImage:nil];
// 3.返回cell
return cell;
}
#pragma mark - UITableViewDelegate
// 选中某一行后播放视频
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
// 获取视频URL
NSDictionary *dict = self.videos[indexPath.row];
NSString *urlStr =[NSString stringWithFormat:@"http://120.25.226.186:32812/%@", dict[@"url"]];
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
// 加载视频播放控制器
MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
// 显示控制器
[self presentViewController:vc animated:YES completion:nil];
}
5.DOM解析-NSXMLParser
- 由于GDataXML基于libxml2库的,而且是MRC的,所以需要在使用GDataXML的项目中做以下配置
- 导入libxml2库
- 设置libxml2的头文件搜索路径
- 设置链接参数(自动链接libxml2库)
- 由于GDataXML是非ARC的,因此得设置编译参数
- DOM解析实例(以此前SAX解析的例子)
// 1.获取网络数据
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/video?type=XML"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// 2.解析XML数据(DOM)
// 2.1创建解析器
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:data options:kNilOptions error:nil];
// 2.2 获取根元素
GDataXMLElement *rootElement = [doc rootElement];
// 2.3从根元素中获取所以子元素
NSArray *elementArr = [rootElement elementsForName:@"video"];
// 2.4遍历数组将数组中的根元素转换为字典(模型)
for (GDataXMLElement *element in elementArr) {
NSMutableDictionary *video = [NSMutableDictionary dictionary];
video[@"id"] = @([element attributeForName:@"id"].stringValue.integerValue);
video[@"image"] = [element attributeForName:@"image"].stringValue;
video[@"url"] = [element attributeForName:@"url"].stringValue;
video[@"name"] = [element attributeForName:@"name"].stringValue;
video[@"length"] = @([element attributeForName:@"length"].stringValue.integerValue);
[self.videos addObject:video];
}
// 2.5 刷新数据
[self.tableView reloadData];
}];
}
6.XML和JSON对比
- 同一份数据,既可以用JSON来表示,也可以用XML来表示
- 相比之下,JSON的体积小于XML,所以服务器返回给移动端的数据格式以JSON居多
- 目前市面上80%以上的网络数据都是JSON格式的,但是我们最终还是要看公司后台返回的是哪种格式的数据,根据接口文件解析对应数据即可