玩转iOS开发-JSON和Xml数据解析
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
玩转iOS开发-JSON和Xml数据解析
前⾔
Json 和xml是⽹络开发中经常使⽤的数据格式,JSON轻量级。
xml相对较复杂。
所以如今⽤JSON的⽐例很⼤。
基本上从server获取的返回数据都是JSON格式的,作为iOS开发⼈员,解析JSON, XML⽂件是⽹络开发最主要的⼀步,不扯蛋了,直接进⼊正题。
JSON解析
JSON介绍
JSON 本质上,就是⼀个”特殊格式”的字符串
JSON 是⽹络上⽤来数据传输使⽤最⼴泛的数据格式,没有之中的⼀个
JSON 出⾝草根,是Javascript的⼦集,专门负责描写叙述数据格式
JSON 语法规则
数据以 key/value 值对表⽰
数据由逗号分隔
花括号保存对象 (字典)
⽅括号保存数组
JSON 存储值
数字(整数或浮点数)
字符串(在双引號中)
逻辑值(true 或 false)
数组(在⽅括号⾥)
对象(在花括号⾥)
null
以下看条结构清晰的JSON数据,和上⾯JSON的特点相应⼀下:
序列化 & 反序列化
序列化:在向server发送数据之前,将NSArray / NSDictionary转换成⼆进制的过程
反序列化:在从server接收到数据之后。
将⼆进制数据转换成NSArray / NSDictionary的过程
JSON 反序列化
虎嗅新闻接⼝
NSURL *url = [NSURL URLWithString:@"/portal/1/1?
client_ver=6&push_type=iOSRel"];
//反序列化
id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
选项说明
NSJSONReadingMutableContainers = (1UL << 0)容器可变
NSJSONReadingMutableLeaves = (1UL << 1)叶⼦可变
NSJSONReadingAllowFragments = (1UL << 2)顶级节点能够不是 NSArray 或者 NSDictionary
在实际开发中。
获得⽹络的数组或者字典之后,⼀般会做字典转模型!
反序列化的结果是否可变并不重要
选项选择 0,表⽰不论什么附加操作都不做,效率最⾼!
NSJSONSerialization 类
专门负责在 JSON 和 Foundation 对象直接转换的类
能够转换成 JSON 的 Foundation 对象须要具备的条件:
* 顶级节点是 NSArray 或者 NSDictionary
* 全部的对象是 NSString, NSNumber, NSArray, NSDictionary 或者 NSNull
* 全部字典的 key 是 NSString
* NSNumber 不是空或者⽆穷⼤
JSON解析样例
以下是⼀个简单的虎嗅新闻的样例:
请求的URL:
返回JSON数据并对JOSN数据解析
打印出第⼀条新闻
我们先来看下URL实际返回的JSON是什么样⼦的:
演⽰样例代码⽤swift 写的。
亲们看下Swift 代码是不是⽐OC美丽多了。
⽽且今年swift2.0就要开源了喽,博主难以掩饰对swift的喜爱。
哈哈
有⽊有发现swift的代码使⽤起来和OC很像,使⽤函数的名字基本⼀致。
仅仅只是那个讨厌的[]变成了让⼈喜爱的.语法,⼤爱。
假设对swift感兴趣,能够兴许看下blog中swift系列的⽂章
好了⾔归正传,亲们看下以下的JSON解析是不是很easy:
func readJson()
{
//这个是虎嗅新闻看点的API
let url = NSURL(string: "/portal/1/1?client_ver=6&push_type=iOSRel")!
let request = NSURLRequest(URL: url)
//⽤NSURLConnection 做异步请求
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (_, data, error) -> Void in
if (data == nil || error != nil)
{
println("⽹络不给⼒")
return
}
//依据数据格式能够知道返回的是⼀个字典(有的站点会返回数组,⽐較少见)
let resultDic = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: nil) as! NSDictionary
//获取内容部分,内容的key-> 'content', value:是个数组
let resultArray = resultDic["content"] as! NSArray
//这⾥我们仅仅看第⼀条新闻。
也能够遍历数组查看全部的内容
let result = resultArray[0] as! NSDictionary;
//依据key 提取我们想要的内容
let cityName = result["img"] as! String
let author = result["author"] as! String
let title = result["title"] as! String
let summary = result["summary"] as! String
println("虎嗅新闻 \n作者:\(author)\n标题:\(title) \n摘要:\(summary) ")
}
}
上⾯结果输出:
虎嗅新闻
作者:虎嗅
标题:综艺节⽬⼤爆炸。
电视台和视频站点过得好吗
摘要:要赚钱,还要领导放⼼,观众惬意,可真够难的。
来看下⼿机app上的结果。
是不是⼀样滴:
注意:
上⾯请求使⽤的swift 闭包{ (_, data, error) -> Void in}来进⾏JSON数据解析,代码运⾏的顺序为:readJson函数-> ⽹络异步请求-> readJson 函数运⾏完毕-> server返回Json数据-> 回调闭包中代码块进⾏JSon数据解析。
看吧,闭包和OC的block很想吧。
实际上swift闭包功能更强⼤。
JSON解析-第三⽅框架
常见的 JSON 解析第三⽅框架
JSONKit(最快)
SBJson
TouchJSON
上⾯三个框架的性能依次减少。
所以我们仅仅说⼀下JSONKit
JSONKit
使⽤步骤步骤
1.
2. 导⼊框架⽂件
JSONKit.h
JSONKit.m
3. 设置 MRC 标记(JSONKit 基于MRC的)
选择”项⽬”-”Build Phases”-”Compile Sources”
找到 JSONKit.m ⽽且在 Compiler Flags 中加⼊-fno-objc-arc。
能够告诉编译器,编译 JSONKit.m 时不使⽤ ARC
4. 改动错误
利⽤⾃⼰主动修复功能。
改动两处 isa 的错误
5. 反序列化
使⽤起来很easy。
创建⼀个decoder对象,调⽤解析⽅法就能够
id result = [[JSONDecoder decoder] objectWithData:data];
注意:
JSONKit 仅仅⽀持iOS5.0之前的系统,是MRC下的框架。
所以新项⽬不要使⽤
JSONKit 2012已经停⽌更新。
当时apple官⽅说JSONKit 的效率要⾼于系统⾃带的JSON解析,只是要知道苹果对于⾃⾝系统的优化更新是很快的,眼下的apple原⽣态的JSON解析已经完爆JSONKit了。
假设你如今还在⽤他,建议换成系统⾃⾝的JSON解析。
苹果新系统强制推⼴的速度很快。
随着更新,许多以前的东西会被更好的替代,NSURLConnection 就是⼀个样例。
所以程序员要跟上时代的潮流啊XML解析
XML 介绍
XML的特点,出⾝名门,W3C制定,微软和IBM以前共同⼤⼒推荐过的数据格式
XML 指可扩展标记语⾔(eXtensible Markup Language)
被设计⽤来传输和存储数据
让我来看下XML数据,瞬间有⽊有想感觉信息量好⼤:
<?xml version="1.0" encoding="UTF-8"?
>
<Books>
<Book id="1">
<title>Circumference</title>
<author>Nicholas Nicastro</author>
<summary>Eratosthenes and the Ancient</summary>
</Book>
<Book id="2">
<title>Copernicus Secret</title>
<author>Jack Repcheck</author>
<summary>How the scientific revolution began</summary>
</Book>
<Book id="3">
<title>Angels and Demons</title>
<author>Dan Brown</author>
<summary>Robert Langdon is summoned to a Swiss</summary>
</Book>
</Books>
XML 解析的⽅式
DOM 解析
是在 MAC上使⽤的解析⽅式
内存消耗极⼤,不适⽤于⼿机
iPhone⽆法直接使⽤ DOM ⽅式解析 XML
SAX 解析
是仅仅读的⽅式,从上向下的⽅式解析
是苹果提供的解析⽅式
速度快
NSXMLParser 通过代理实现解析
SAX 解析步骤
1. 開始⽂档 - 准备⼯作
2. 開始”节点”
3. 发现节点内部的内容。
每个节点,可能须要多次才⼲找完
4. 结束”节点”
5. 结束⽂档 - 解析结束
以上步骤,2。
3。
4。
会不断循环,⼀直到全部解析完毕!
事实上解析本质上就是不断循环遍历节点,获取节点内容。
由于XML的节点格式是成对出现的。
只是节点的名字是⾃⼰定义的,这也是解析难点所在。
1. loadData
- (void)loadData {
NSURL *url = [NSURL URLWithString:@"http://localhost/books.xml"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 异步解析 XML
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// 1. 使⽤⽹络返回的⼆进制数据实例化 NSXMLParser 对象
NSXMLParser *ZHParser = [[NSXMLParser alloc] initWithData:data];
// 2. 设置代理,通过代理⽅法实现 SAX 解析
ZHParser.delegate = self;
// 3. 開始解析
[ZHParser parse];
}];
}
使⽤代理⽅法获取数据
仅仅要没有碰到⽂档的结束符。
解析器就会循化运⾏2,3,4⽅法,直到所以后的数据解析完
// MARK: - NSXMLParserDelegate
// 1. 開始⽂档 - 准备⼯作
- (void)parserDidStartDocument:(NSXMLParser *)parser {
NSLog(@"1. 開始⽂档");
}
// 2. 開始节点
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { NSLog(@"2. 開始节点 %@ %@", elementName, attributeDict);
}
// 3. 发现⽂字
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
NSLog(@"==> %@", string);
}
// 4. 结束节点
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
NSLog(@"4. 结束节点 %@", elementName);
}
// 5. 结束⽂档 - 解析结束
- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSLog(@"5. 解析结束");
}
// 6. 错误处理
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSLog(@"解析错误 %@", parseError);
}
SAX & DOM 解析对照
SAX 特点
仅仅读
从上向下
速度快
解析的时候相对照较繁琐,有5个代理⽅法。
每个代理⽅法都要写⼀定代码
适合⼤的 XML ⽂件解析
总的来说。
SAX仅仅在xml⽂档中查找特定条件的内容。
⽽且仅仅提取须要的内容。
这样做占⽤内存⼩,灵活。
DOM 特点
背景
主要⽤在 PC 端或者server端
苹果提供了NSXML类⽀持DOM⽅式的解析
只是NSXML类仅仅能⽤在MAC开发。
在iOS中⽆法直接使⽤
DOM ⽅式不仅能解析 XML ⽂档,还能够改动: 添加节点/删除节点
实现
⼀次性将 XML ⽂档以树形结构读⼊内存
横向的节点越多,内存消耗越⼤
使⽤ DOM 解析适合于⼩的 XML 解析,⽽且能够动态维护
有些第三⽅框架就提供了 DOM ⽅式的解析,GData/KissXML(XMPP)
在 iOS 开发中,假设要使⽤ DOM ⽅式解析,最好仅仅处理⼩的 XML
GData解析XML
GDataXMLNode是Google提供的⽤于XML数据处理的类集。
该类集对libxml2-DOM处理⽅式进⾏了封装,能对较⼩或中等的XML⽂档进⾏读写操作且⽀持XPath语法。
原理上和DOM同样。
GData解析步骤
获得根节点。
依次 Log,⼀定要确认能够拿到全部⼦节点的内容
横向节点越多,for的层次就越深
依据实际的 XML 的情况,确认解析
准备⼯作
訪问 , 获得GDataXMLNode.h和GDataXMLNode.m ⽂件
将GDataXMLNode.h/m⽂件加⼊到project中
向project中添加“libxml2.dylib”库。
在project的“Build Settings”页中找到“Header Search Path”项,加⼊“/usr/include/libxml2”到其路径GData使⽤演⽰样例
XML数据
<?xml version="1.0"?>
<xml_api_reply version="1">
<cities>
<city>
<name data="保定"/>
<latitude_e6> 38849998</latitude_e6>
<longitude_e6> 115569999</longitude_e6>
</city>
<city default="true" >
<name data="北京"/>
<latitude_e6> 39930000</latitude_e6>
<longitude_e6> 116279998</longitude_e6>
</city>
<city>
<name data="沈阳"/>
<latitude_e6> 41770000</latitude_e6>
<longitude_e6> 123430000</longitude_e6>
</city>
<city>
<name data="成都"/>
<latitude_e6> 30670000</latitude_e6>
<longitude_e6> 104019996</longitude_e6>
</city>
<city>
<name data="⼤连"/>
<latitude_e6> 38900001</latitude_e6>
<longitude_e6> 121629997</longitude_e6>
</city>
<city>
<name data="福州"/>
<latitude_e6> 26079999</latitude_e6>
<longitude_e6> 119279998</longitude_e6>
</city>
</cities>
</xml_api_reply>
解析⽅法
⾸先分析下数据层次结构:
<?
xml version="1.0"?> : </xml_api_reply>
<cities> : </cities>
<city> : </city>
依据上⾯的节点来解析数据。
要有两个for循环
- (void)parseCitys
{
NSData *xmlCitysData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"citys" ofType:@"xml"]]; // 读取data在内存中形成完整的树形结构
NSError * error = nil;
GDataXMLDocument * documents = [[GDataXMLDocument alloc]initWithData:xmlCitysData options:0 error:&error];
// 取得根节点
GDataXMLElement *rootNode = [documents rootElement];
//获取根节点xml_api_reply的数组,包括的是cities,假设根节点不知有⼀个数组。
就⼜要遍历
NSArray * citiesArray = [rootNode elementsForName:@"cities"];
for (int i = 0; i < [citiesArray count]; i++) {
// 取得单个cities节点
GDataXMLElement * cities = [citiesArray objectAtIndex:i];
//获取节点cities的数组,包括的是city
//看出规律了吧,通过当前GDataXMLElement和⼦节点的name,能够获取⼦节点的数组
NSArray * cityArray = [cities elementsForName:@"city"];
for (int j = 0; j < [cityArray count]; j++) {
// 取得单个city节点
GDataXMLElement * city = [cityArray objectAtIndex:j];
//节点以下是3个并⾏的节点,都是1个,直接通过lastObject来取得想要的值
NSString * name = [[[[city elementsForName:@"name"]lastObject] attributeForName:@"data"] stringValue];
NSLog(@"name = %@",name);
NSString * latitude_e6 = [[[city elementsForName:@"latitude_e6"]lastObject] stringValue];
NSLog(@"latitude_e6 = %@",latitude_e6);
NSString * longitude_e6 = [[[city elementsForName:@"longitude_e6"]lastObject] stringValue];
NSLog(@"longitude_e6 = %@",longitude_e6);
}
}
}
PList解析
PList 主要在苹果开发中经常使⽤。
⽐⽅那个新建项⽬都会⾃带⼀个info.plist,事实上全部的数据格式都是相通的,⽆⾮存储数组。
字典。
对象,好的数据格式就是易于存储,易于读写。
这⾥最后简介下plist, 毕竟⼤部分server后台并不会返回 PList 的数据格式。
/**
參数
1. data: 要反序列化的⼆进制数据
2. option: 选项,位移枚举类型
NSPropertyListImmutable = 0, 不可变
NSPropertyListMutableContainers = 1, 容器可变
NSPropertyListMutableContainersAndLeaves = 2 容器和叶⼦可变
3. format: 假设不希望知道格式。
传⼊ NULL 就可以
4. error: 错误
*/
id result = [NSPropertyListSerialization propertyListWithData:data options:0 format:NULL error:NULL];
兴许阅读。