一 什么是解析?
1.解析:按照约定好的格式提取数据 就是解析
2.数据的提供方:后台开发人员 按照约定好的格式存入数据
数据的提取方:按照约定好的格式提出数据
3.目前主流格式:XML/JSON
① XML 以标签的形式 管理数据 使用比较长的一种方式 比较复杂 逐渐被代替中
/*
1.标签包括:开始标签 结束标签,一对开始和结束标签叫节点;
2.没有父节点的节点叫做根节点,没有子节点的节点叫做叶子节点;我们要取的数据一般都在叶子节点里
3.解析的两种形式:
3.1 SAX 解析 (适合解析大数据 大数据喜欢吃内存 ) NSXMLParser
基于事件回调的形式解析,自己不想做 让别人做就是回调 找代理 ,逐行解析 系统提供的就是这样的
优点:对内存占有率较低
缺点:效率低 适合大数据
3.2 DOM 解析
解析时,会把数据解析到内存中,把读入的数据初始化成树状结构,然后一层一层的读取,适用于小数据 谷歌提供的 GDataXMLNode
*/
②JSON 即 JavaScript Object Natation,它是一种轻量级的数据交换格式,非常适合于服务器与客户端的交互 ,逐渐代替 XML
二 XML
1. 首先看一下 xml 的 数据源
<?xml version = "1.0" encoding = "UTF-8"?>
<lanou27>// 开始标签
<Student position = "团一波"> //开始标签
<name>Nyx</name>
<gender>Girl</gender>
<age>18</age>
<say>我们来吧</say>
</Student>//结束标签
<Student position = "等我大招">//开始标签
<name>Sona</name>
<gender>Girl</gender>
<age>18</age>
<say>我不会说话啊</say>
</Student>//结束标签
<Student position = "看我一挑五">//开始标签
<name>瑞萌萌</name>
<gender>Girl</gender>
<age>19</age>
<say>断剑重铸之日,骑士归来之时</say>
</Student>//结束标签
<Student position = "我的小熊呢!">//开始标签
<name> Anne</name>
<gender>Girl</gender>
<age>15</age>
<say>看见我的小熊了么</say>
</Student>//结束标签
</lanou27>//结束标签
注:带有斜杠的代码 我们定义为结束标签,反之不带则是开始标签
2.解析过程
①SAX(SimpleAPI for XML)
(适合解析大数据 大数据喜欢吃内存 ) NSXMLParser
基于事件回调的形式解析,自己不想做 让别人做就是回调 找代理 ,逐行解析 系统提供的就是这样的
只能读,不能修改,只能顺序访问,适合解析大型XML,解析速度快,实现异构系统的数据访问,实现跨平台
从文档的开始通过每一节点移动,定位一个特定的节点
优点:对内存占有率较低
缺点:效率低 适合大数据
②XML解析步骤:
1、实例化NSXMLParser,传入从服务器接收的XML数据,我们刚在上面定义的 XML 就可以在代码中使用
2、定义解析器代理
常用的代理方法:
1. 开始解析XML文档
- (void)parserDidStartDocument:(NSXMLParser *)parser;
2. 开始解析某个元素,会遍历整个XML,识别元素节点名称,如<Student>开头
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict;
3. 文本节点,得到文本节点里存储的信息数据。 节点中的数据<name>NYX</name> 这里不可以赋值
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
4. 结束某个节点 如</Student>开头
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
注意:在解析过程中,2、3、4三个方法会不停的重复执行,直到遍历完成为止
5.解析XML文档结束
- (void)parserDidEndDocument:
3、解析器解析,通过解析代理方法完成XML数据的解析。
下面是代码;
#import "SAXTableViewController.h"
#import "StudentMode.h"
@interface SAXTableViewController ()<NSXMLParserDelegate>//设置代理需要遵守的协议
@property (nonatomic,retain)NSMutableArray *dataSoutse;
@property(nonatomic,retain)StudentMode *stu;
@property (nonatomic,copy)NSString *string;
@end
@implementation SAXTableViewController
//我们用的 MRC 所以要手动释放
- (void)dealloc
{
[_dataSoutse release];
[_string release];
[_stu release];
[super dealloc];
}
//程序运行会走的方法
- (void)viewDidLoad {
[super viewDidLoad];
// 注册 cell 完成我们想要实现的 button 功能
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"reuseIdentifier"];
[self configureNavigationBar];
}
// 注:网上请求的数据不可以懒加载
#pragma mark -----懒加载-----lazy-loading
-(NSMutableArray *)dataSoutse{
if (!_dataSoutse) {
self.dataSoutse =[NSMutableArray arrayWithCapacity:1];
}return [[_dataSoutse retain]autorelease];
}
//添加 button 属性 完成点击事件
-(void)configureNavigationBar{
UIBarButtonItem *rightItem =[[UIBarButtonItem alloc]initWithTitle:@"解析" style:(UIBarButtonItemStylePlain) target:self action:@selector(HandlePrase:)];
self.navigationItem.rightBarButtonItem =rightItem;
[rightItem release];
}
// 我们的点击事件 通过点击 button 完成将需要的值传入界面中
-(void)HandlePrase:(UIBarButtonItem *)sender{
//使用系统提供的 NSXMLParser 解析,解析原理:
// 1.读取文件路径
NSString *filePath =[[NSBundle mainBundle]pathForResource:@"Students" ofType:@"xml"];
// 2.读取数据
NSData *data =[NSData dataWithContentsOfFile:filePath];
// 3.创建解析对象
NSXMLParser *parser =[[NSXMLParser alloc]initWithData:data];
// 4.设置代理
parser.delegate =self;
// 5.开始解析
[parser parse];
[parser release];
}
#pragma mark----解析代理方法的实现------
//开始解析文档会触发的方法
- (void)parserDidStartDocument:(NSXMLParser *)parser{
// 下拉刷新之前需要将上次的东西清空 这样当你刷新的时候就不会重复出现同样的内存 不停的扩充你的indexPath.row;
self.dataSoutse =nil;
}
//结束解析文档会触发的方法
- (void)parserDidEndDocument:(NSXMLParser *)parser{
// 重新加载数据 刷新作用
[self.tableView reloadData];
}
//当读取到开始标签的时候触发 比如:<Student> 不带/的
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
NSLog(@"%@",elementName);
//设定的 position 里的值是以键值对的形态存在 存放在attributeDict
if ([elementName isEqualToString:@"Student"]) {
self.stu =[[StudentMode alloc]init];
//存储开始节点的属性值
self.stu.position =attributeDict[@"position"];
}
}
//当读取到标签内容后触发的方法, 比如:Nyx 不能在这里赋值
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
self.string =string;
}
//读取到结束标签的时候会触发的方法 ,比如:</Student>
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
// 这就是赋值的过程
if ([elementName isEqualToString:@"name"]) {
self.stu.name =self.string;
}else if ([elementName isEqualToString:@"age"]){
self.stu.age =self.string;
}else if ([elementName isEqualToString:@"gender"]){
self.stu.gender =self.string;
}else if ([elementName isEqualToString:@"say"]){
self.stu.say =self.string;
}else if ([elementName isEqualToString:@"Student"]){
[self.dataSoutse addObject:self.stu];
}
}
总结:
SAX解析XML,是基于事件通知的模式,一边读取XML文档一边处理,不必等整个文档加载完之后才采取操作,当在读取解析过程中遇到需要处理的对象,会发出通知对其进行处理。
2.DOM 解析模型就像一个树结构,根节点 ,叶子节点;
GDataXMLNode 是Google 提供的开源的类库,底层有 c 语言实现, < libxml.2.dylib > (动态链接库),C 语言把这个动态链接类库封装成了 OC 中的 XML 解析类,效率比较高;
① GDataXMLNode 使用步骤:
1 taget–Build phases–> link Binary–> 添加一个 libxml2.dylib;
2 taget–>BuildSetting –>Header seacher –>添加了”/usr/include/libxml2”;
libxml2.dylib 相当于一个快捷方式 ,永远都是指向的是 最新的类库, libxml2.2才是真实的类库,当系统类库更新的适合,老版本的 libxml2.dylib 就不需要导入更新后的类库
② DOM 分两种:一种是绝对路径解析 就是只有一条路到达终点;一种是相对路径解析 有多中方法可以达到目的
①布局
#import "DOMTableViewController.h"
#import "StudentMode.h"
#import "GDataXMLNode.h"
@interface DOMTableViewController ()
@property (nonatomic,retain)StudentMode *stu;
@property (nonatomic,retain)NSMutableArray *dataSource;
@end
@implementation DOMTableViewController
- (void)dealloc
{
[_dataSource release];
[_stu release];
[super dealloc];
}
- (void)viewDidLoad {
[super viewDidLoad];
//注册 cell
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
[self configuerRight];
[self configuerLeft ];
}
#pragma mark ------lazyLoading------
//重写 setter 方法 懒加载
-(NSMutableArray *)dataSource{
if (!_dataSource) {
self.dataSource =[NSMutableArray arrayWithCapacity:1];
}return [[_dataSource retain] autorelease];
}
//根节点 绝对路径
-(void)configuerLeft{
UIBarButtonItem *leftItem =[[UIBarButtonItem alloc]initWithTitle:@"RootParser" style:(UIBarButtonItemStylePlain) target:self action:@selector(HandleRootParser:)];
self.navigationItem.leftBarButtonItem =leftItem;
[leftItem release];
}
// 相对路径 XPath
-(void)configuerRight{
UIBarButtonItem *rightItem =[[UIBarButtonItem alloc]initWithTitle:@"XPath" style:(UIBarButtonItemStylePlain) target:self action:@selector(HandleXPath:)];
self.navigationItem.rightBarButtonItem =rightItem;
[rightItem release];
}
根节点解析
//通过根节点进行解析
-(void)HandleRootParser:(UIBarButtonItem *)sender{
#warning 清空之前的数据
self.dataSource = nil;
// [self.dataSource removeAllObjects]; 也可以达到效果
// 1.获取文件路径
NSString *filePath =[[NSBundle mainBundle]pathForResource:@"Students" ofType:@"xml"];
// 2.根据文件路径初始化 字符串对象
NSString *xmlStr =[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
//3.将解析内容写入 GDataXMLDocment 这个文档 等于存入了内容;
GDataXMLDocument *docment =[[GDataXMLDocument alloc]initWithXMLString:xmlStr options:0 error:nil];
//GDataXMLElement (节点对象)
// 4.获取文档中的节点对象 -->GDataXMLElement;获取根节点
// 不仅会打印根节点 根节点包括的东西也会打印出来
GDataXMLElement *rootElement =[docment rootElement];
// NSLog(@"%@",rootElement);
// 5.获取根节点下的所有的 Student 节点
NSArray *studentArr=[rootElement elementsForName:@"Student"];
// 6.对数组遍历获取每个学生的节点信息
for (GDataXMLElement *element in studentArr) {
// 7.获取 Student 下的叶子节点 就是他的属性
// name 节点
GDataXMLElement *nameElement =[[element elementsForName:@"name"] firstObject];
// age
GDataXMLElement *ageElement =[[element elementsForName:@"age"] firstObject];
// gender
GDataXMLElement *genderElement =[[element elementsForName:@"gender"] firstObject];
// say
GDataXMLElement *sayElement =[[element elementsForName:@"say"] firstObject];
// 8 创建学生对象 对其赋值
StudentMode *stu =[[StudentMode alloc]init];
stu.name =[nameElement stringValue];
stu.age =[ageElement stringValue];
stu.gender =[genderElement stringValue];
stu.say =[sayElement stringValue];
//9 获取节点中属性值,属性即 GDataXMLNode
GDataXMLNode *node =[element attributeForName:@"position"];
stu.position =[node stringValue];
// 10将赋值结束的学生对象 添加到数组 中
[self.dataSource addObject:stu];
[stu release];
}
#warning 解析完之后要刷新
[self.tableView reloadData];
}
注: 解析完的刷新和 之前的数组清空很重要,反之会出现一样的东西不停的重复
XPath 相对路径解析
#pragma mark------相对路径解析----
-(void)HandleXPath:(UIBarButtonItem *)sender{
self.dataSource =nil;
// 1.获取文件路径
NSString *filePath =[[NSBundle mainBundle]pathForResource:@"Students" ofType:@"xml"];
// 2.根据文件路径初始化 字符串对象
NSString *xmlStr =[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
//3.将解析内容写入 GDataXMLDocment 这个文档 等于存入了内容;
GDataXMLDocument *docment =[[GDataXMLDocument alloc]initWithXMLString:xmlStr options:0 error:nil];
//4. 获取所有的 name 节点 这里是相对路径 只要能找到这个那么就好
#warning 一个/ 是绝对路径 两个// 是相对路径
NSArray *nameElements= [docment nodesForXPath:@"//name" error:nil];
NSArray *ageElements =[docment nodesForXPath:@"//age" error:nil];
NSArray *genderElements =[docment nodesForXPath:@"//gender" error:nil];
NSArray *sayElements =[docment nodesForXPath:@"//say" error:nil];
// 根节点
NSArray *stuElements =[docment nodesForXPath:@"//Student" error:nil];
int count = (int )nameElements.count;//强制转换
// 5.遍历获取学生信息
for (int i =0; i <count; i++) {
GDataXMLElement *nameElement = nameElements[i];
GDataXMLElement *ageElement =ageElements[i];
GDataXMLElement *genderElement =genderElements[i];
GDataXMLElement *sayElement =sayElements[i];
GDataXMLElement *stuElement =stuElements[i];
// 6.取出节点中的值 为 stu 赋值
StudentMode *stu =[[StudentMode alloc]init];
stu.name = [nameElement stringValue];
stu.age =[ageElement stringValue];
stu.gender =[genderElement stringValue];
stu.say =[sayElement stringValue];
//获取节点中属性值,属性 GDataXMLNode
GDataXMLNode *node=[stuElement attributeForName:@"position"];
// 为属性赋值
stu.position =[node stringValue];
// 6 添加到数组中
[self.dataSource addObject:stu];
[stu release];
}
[self.tableView reloadData];
}
总结:
1.根节点和 XPath 都是通过遍历 赋值 来找到你想要的数据
2.别忘了 在设定 section 和 row 的方法中开辟空间
3.重用 cell 方法
三 JSON 非常便利的解析方法 他的数据源和 XML 也不一样
[
{
"name":"Nyx",
"gender":"Girl",
"age":15,
"say":"你是看不起我么"
},
{
"name":"暴风女神",
"gender":"Girl",
"age":"17",
"say":"风之化身听你差遣"
},
{
"name":"堕落天使 莫甘娜",
"gender":"Gilr",
"age":"18",
"say":"他们将会痛苦不堪"
},
{
"name":"狂野女猎手 奶大力",
"gender":"Girl",
"age":"17",
"say":"狂野 将使他们感到畏惧"
},
{
"name":"迅捷斥候 提莫",
"gender":" Boy",
"age":"16",
"say":"我去前面探探路"
}
]
最外层是数组 里面每一组数据是字典
2.解析代码
①系统解析
#import "JSONTableViewController.h"
#import "StudentMode.h"
#import "JSONKit.h"
@interface JSONTableViewController ()
@property (nonatomic,retain)NSMutableArray *dataSource;
@end
@implementation JSONTableViewController
- (void)dealloc
{
[_dataSource release];
[super dealloc];
}
//懒加载 重写 setter 方法
-(NSMutableArray *)dataSource{
if (!_dataSource) {
self.dataSource = [NSMutableArray arrayWithCapacity:0];
}return [[_dataSource retain] autorelease];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self configuerRight];
[self configuerLeft ];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
#pragma mark------系统的解析
-(void)configuerLeft{
UIBarButtonItem *leftItem =[[UIBarButtonItem alloc]initWithTitle:@"系统" style:(UIBarButtonItemStylePlain) target:self action:@selector(HandleSystem:)];
self.navigationItem.leftBarButtonItem =leftItem;
[leftItem release];
}
-(void)HandleSystem:(UIBarButtonItem *)sender{
self.dataSource = nil;
// 1.获取文件路径
NSString *filePath =[[NSBundle mainBundle]pathForResource:@"Student"ofType:@"json"];
// 2.根据文件路径 初始化 NSData 对象
NSData *data= [NSData dataWithContentsOfFile:filePath];
// 3.解析了
NSMutableArray *arr =[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
self.dataSource =[NSMutableArray arrayWithCapacity:1];
for (NSDictionary *dic in arr) {
StudentMode *stu =[[StudentMode alloc]init];
[stu setValuesForKeysWithDictionary:dic];
[self.dataSource addObject:stu];
[stu release];
}
[self.tableView reloadData];
}
② 第三方解析 需要引入你的第三方 我在这里用的是 JSONKit
#pragma mark-----第三方解析--------
-(void)configuerRight{
UIBarButtonItem *rightItem =[[UIBarButtonItem alloc]initWithTitle:@"第三方" style:(UIBarButtonItemStylePlain) target:self action:@selector(HandThirdPrase:)];
self.navigationItem.rightBarButtonItem =rightItem;
[rightItem release];
}
-(void)HandThirdPrase:(UIBarButtonItem *)sender{
self.dataSource = nil;
//JSONKit通过对NSString,NSData添加分类的形式,添加解析的功能
//JSONKit 通过对NSString ,NSData添加分类的形式,添加了一个解析的功能
//1,获取文件路径
NSString *filePath = [[NSBundle mainBundle ]pathForResource:@"Student" ofType:@"json"];
//2,根据文件路径初始化字符串对象
NSString *dataStr = [NSString stringWithContentsOfFile:filePath encoding:(NSUTF8StringEncoding) error:nil];
//3,解析OC对象,(返回值可以选择可变或不可变)
//dataStr 调用分类的方法
NSArray *arr1 = [dataStr objectFromJSONString];
// NSLog(@"%@",arr1);
NSMutableArray *arr2 = [dataStr mutableObjectFromJSONString];
// NSLog(@"%@",arr2);
//根据文件路径初始化NSData 对象
NSData *data = [NSData dataWithContentsOfFile:filePath];
// NSLog(@"%@",data);
// 解析 (可变/不可变) 取不到数据????
NSArray *arr3 = [data objectFromJSONData];
// NSLog(@"%@",arr3);
NSMutableArray *arr4 = [data mutableObjectFromJSONData];
// NSLog(@"%@",arr4);
self.dataSource =[NSMutableArray arrayWithCapacity:1];
for (NSDictionary *dic in arr4) {
StudentMode *stu =[[StudentMode alloc]init];
[stu setValuesForKeysWithDictionary:dic];
[self.dataSource addObject:stu];
[stu release];
}
[self.tableView reloadData];
}
总结:
1.所有的解析数据 步骤大致:
①获取文件地址
②根据文件初始化你所订的对象类型
③存入数据
④遍历
⑤赋值
2. 方法的清空和刷新 是每一个解析方法必备!
3. XML 的方法 需要多练习掌握