iOS-进阶整理04 - JSON与xml文件解析
一、JSON与XML的优缺点
XML与JSON共同点:
1.格式统一,符合标准
2.容易与其他系统继续远程交互,数据共享比较方便
缺点:
1.XML文件格式文件庞大,格式赋值,传输占用带宽
2.服务器端和客户端都需要花费大量的代码来解析XML,不论服务器还是客户端都使代码变得异常复杂不易维护
3.客户端不同浏览器直接解析XML的方式不一致,需要重复编写很多代码
3.服务器端和客户端解析XML花费资源和数据
JSON
优点:
1.数据格式比较简单,易于读写,格式都是压缩的,占用带宽小
2.易于解析这种语言
3.支持多种语言,包括ACtionScript,C,C#,java,PHP,Python等,方便服务器端解析
4.以为JSON格式能够直接为服务器端代码使用,大大简化了服务器端的代码量,且易于维护
缺点:
1.没有XML格式推广的深入人心和使用广泛
2.JSON在Web Service中推广还属于初级阶段。
二、JSON解析
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,才有完全独立于语言的文本格式,易于阅读和编写,与易于解析和生成。
JSON文件有两种结构:
对象:“名称/值”对的集合,可以理解为字典
数组:值的有序列表
NSJSONSerializationi里面包含了两种方法来通过不同的数据形式解析JSON数据
//解析
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSDictionary *dic2 = [NSJSONSerialization writeJSONObject:<#(nonnull id)#>
toStream:<#(nonnull NSOutputStream *)#> options:<#(NSJSONWritingOptions)#>
error:<#(NSError * _Nullable __autoreleasing * _Nullable)#>]
//通用的json解析方法
//有时json文件前面会有一些不属于json的字符,人为去除
-(void)normalJsonData
{
//得到路径
NSString *path = [[NSBundle mainBundle]pathForResource:@"MovieList" ofType:@"txt"];
//由于不确定是否为标准格式的json串,先用字符串来接收文件数据
NSString *fileStr = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//判断字符串是否存在并且是以 { 或者 [ 开始的,,是则说明文件为标准json串,否则需要截取字符
if (!(fileStr && ([fileStr hasPrefix:@"{"] || [fileStr hasPrefix:@"["])) )
{
//声明不是标准格式json,找到第一个{ 和 [ 的位置
NSRange range = [fileStr rangeOfString:@"{"];
NSRange range_1 = [fileStr rangeOfString:@"["];
NSLog(@"%@,%@",NSStringFromRange(range),NSStringFromRange(range_1));
if (range.location > range_1.location)
{
// 中括号 [ 开头从中括号位置开始截取
fileStr = [fileStr substringFromIndex:range_1.location];
}
else
{ // 花括号{ 开头
fileStr = [fileStr substringFromIndex:range.location];
}
}
//经过处理fileStr为标准的json串
NSData *resultData = [fileStr dataUsingEncoding:NSUTF8StringEncoding];
//解析
id resultValue = [NSJSONSerialization JSONObjectWithData:resultData options:NSJSONReadingAllowFragments error:nil];
NSLog(@"id = %@",resultValue);
}
三、XML解析
XML有两种解析方法:DOM(Document Object Model)解析和SAX(Simple API for XML)工具
1.DOM解析XML,使用GDataXMLNode文件
DOM解析时,读入整个XML文档并构建一个驻留内存的树结构(节点树),通过遍历树结构可以检索任意XML节点,读取属性和值。
通常可以借助XPath,直接查询XML节点
iOS包含一个C语言动态链接库libxml2.tbd(xcode7以前为libxml2.dylib)
GDataXMLNode是Google提供的开元XML解析类,对libxml2.tbd进行了Objective-C的封装,能对较小或中等的xml稳定进行读写操作并指出XPath语法。
(1)拖入GDataXMLNode的.h和.m文件到工程
这时候进行编译,会报错"libxml/tree/h file not found"
(3)在工程的Build Settings设置里面搜索search,找到Search paths选项下的Header Search Paths,加入一条/usr/include/libxml2这里三个反斜杠不能少
这时候进行编译,可能会出现20个左右的错误,因为GDataXMLNode.m文件使用的是MRC模式,如果我们的工程是ARC模式下的,是不能使用autorelease等内存操作的。
(4)再进入Build phases设置的Compile Sources选项中,选择GDataXMLNode.m文件,点击文件后面的空白,添加一句-fno-objc-arc。
这时候再编译,终于没啥问题了,可以开始用了,别嫌麻烦,sax不用配置但用起来麻烦
要解析的XML文件为下面的内容文件名为XMLDemo.xml
<?xml version="1.0" encoding="utf-8"?> <!--此行包含XML的版本信息和编码格式-->
<students><!--这是开始标签,也就是根节点-->
<student attribute = "吉祥物"><!--student为根节点的子节点,name节点的父节点, attribute是它的属性-->
<name>李帅</name><!--洛洛受为name节点的值-->
<sex>无</sex>
<age>14</age>
</student>
<student attribute = "吉祥物之基友">
<name>建华</name>
<sex>随条件改变</sex>
<age>17</age>
</student>
</students><!--节点的结束标签都是以/加标签名称组成 -->
//dom解析
-(void)domParser
{
//获得文件路径
NSString *path = [[NSBundle mainBundle]pathForResource:@"XMLDemo" ofType:@"xml"];
//用NSData接收
NSData *xmlData = [NSData dataWithContentsOfFile:path];
//将文件类型通过note对象转换为树形结构(一次性从内存中将XML文件转换为倒着的树形结构)
GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:xmlData options:0 error:nil];
//得到根节点
GDataXMLElement *rootElement = [doc rootElement];
//得到根节点的所有节点,这个方法很重要,通过这个方法一层层得到子节点
NSArray *stuElement = [rootElement elementsForName:@"student"];
//初始化一个可变数组,用来存放每个学生的信息
NSMutableArray *allStudentMArray = [[NSMutableArray alloc]init];
//得到student节点的所有子节点
for (GDataXMLElement* itemElement in stuElement) {
//itemElement是student节点,分别取出student的子节点
NSArray *nameElement = [itemElement elementsForName:@"name"];
NSString *name = [[nameElement objectAtIndex:0]stringValue];
NSArray *sexElement = [itemElement elementsForName:@"sex"];
NSString *sex = [[sexElement objectAtIndex:0]stringValue];
NSArray *ageElement = [itemElement elementsForName:@"age"];
NSString *age = [[ageElement objectAtIndex:0]stringValue];
NSDictionary *dic = [[NSDictionary alloc]initWithObjectsAndKeys:name,@"name",sex,@"sex",age,@"age", nil nil];
[allStudentMArray addObject:dic];
//得到节点的属性
NSArray *attributes = itemElement.attributes;
//得到属性节点
GDataXMLNode *node = [itemElement attributeForName:@"attribute"];
NSLog(@"attr = %@ , node.name = %@ node.stringValue = %@ ",attributes[0],node.name,node.stringValue);
}
NSLog(@"%@",allStudentMArray);
}
//通过dom解析的方式为xml增加节点(sax解析只可以读取,不可以添加)
-(void)domAddNote
{
//获得文件路径
NSString *path = [[NSBundle mainBundle]pathForResource:@"XMLDemo" ofType:@"xml"];
//用NSData接收
NSData *xmlData = [NSData dataWithContentsOfFile:path];
//得到
GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:xmlData options:0 error:nil];
//得到根节点
GDataXMLElement *rootElement = [doc rootElement];
//创建一个我们需要添加的节点(student)
GDataXMLElement *createElement = [GDataXMLNode elementWithName:@"student"];
GDataXMLElement *nameNode = [GDataXMLElement elementWithName:@"name" stringValue:@"成功"];
[createElement addChild:nameNode];
GDataXMLElement *sexNode = [GDataXMLElement elementWithName:@"sex" stringValue:@"男"];
[createElement addChild:sexNode];
GDataXMLElement *ageNode = [GDataXMLElement elementWithName:@"age" stringValue:@"11"];
[createElement addChild:ageNode];
//将创建好的student节点添加到根节点
[rootElement addChild:createElement];
// [rootElement removeChild:createElement];//删除
//得到所有的student节点
NSArray *stuElementArray = [rootElement elementsForName:@"student"];
//遍历根节点
for (GDataXMLElement *stuItem in stuElementArray) {
//name
NSString *name = [[[stuItem elementsForName:@"name"]objectAtIndex:0]stringValue];
NSString *sex = [[[stuItem elementsForName:@"sex"]objectAtIndex:0]stringValue];
NSString *age = [[[stuItem elementsForName:@"age"]objectAtIndex:0]stringValue];
NSLog(@"name = %@,sex = %@,age = %@",name,sex,age);
}
}
2.SAX解析
SAX是基于事件驱动的解析方式,逐行进行事件解析(采用协议回调机制)
解析过程:开始标签->取值->结束标签->取值
//xml的sax解析,逐行解析
-(void)saxParser
{
NSString *path = [[NSBundle mainBundle]pathForResource:@"XMLDemo" ofType:@"xml"];
NSData *data = [NSData dataWithContentsOfFile:path];
//sax解析
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
//指定代理
parser.delegate = self;
//开始解析(同步,解析未完成,下面的代码就不会执行
BOOL isSuccess = [parser parse];
if (isSuccess) {
NSLog(@"sax ok");
}
else
{
NSLog(@"sax fs");
}
}
SAX解析的代理方法
//导入协议NSXMLParserDelegate
@interface RootViewController ()<NSXMLParserDelegate>
#pragma mark -- sax解析的代理方法
//开始解析
-(void)parserDidStartDocument:(NSXMLParser *)parser
{
NSLog(@"开始解析");
self.allStudentsMArray = [[NSMutableArray alloc]init];
}
//解析结束
-(void)parserDidEndDocument:(NSXMLParser *)parser
{
NSLog(@"解析结束");
NSLog(@"array -- %@",self.allStudentsMArray);
}
//开始解析节点
//elementName:标签名称
//namespaceURI;命名空间指向的链接
//qName:命名空间代名词
//attributeDict:节点的属性值
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict
{
NSLog(@"开始解析节点--%@",elementName);
if ([elementName isEqualToString:@"student"]) {
//当解析到student的时候,说明已经到了该获取该节点子节点的值的时候,应该初始化容器了
self.singleStudentDic = [NSMutableDictionary dictionary];
}
}
//string为所需要的值的一部分
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (self.noteValueMstring) {
//对解析数据进行拼接
[self.noteValueMstring appendString:string];
NSLog(@"%@",_noteValueMstring);
}
else
{
self.noteValueMstring = [NSMutableString stringWithString:string];
}
}
//节点结束
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:@"name"]) {
//说明name取值结束
[_singleStudentDic setObject:[self replaceStringWith:_noteValueMstring] forKey:elementName];
}
if ([elementName isEqualToString:@"age"]) {
//说明age节点取值完成
[_singleStudentDic setObject:[self replaceStringWith:_noteValueMstring] forKey:elementName];
}
if ([elementName isEqualToString:@"sex"]) {
[_singleStudentDic setObject:[self replaceStringWith:_noteValueMstring] forKey:elementName];
}
if ([elementName isEqualToString:@"student"]) {
//说明student的解析结束了,该把字典放入数组了
[self.allStudentsMArray addObject:_singleStudentDic];
// [_singleStudentDic removeAllObjects];在开始解析节点的时候对dic进行了初始化
}
//每次解析完成一个节点,都需要将可变字符串清理一次
_noteValueMstring = nil;
}
//替换特殊字符,SAX解析会把XML中的各种换行和tab空格等字符解析出来,人为修改一下
-(NSString*)replaceStringWith:(NSString*)sourceStr
{
NSString *resultStr = [sourceStr stringByReplacingOccurrencesOfString:@"\r" withString:@""];
resultStr = [resultStr stringByReplacingOccurrencesOfString:@"\n" withString:@""];
resultStr = [resultStr stringByReplacingOccurrencesOfString:@"\t" withString:@""];
resultStr = [resultStr stringByReplacingOccurrencesOfString:@" " withString:@""];
return resultStr;
}
这么看来还是DOM解析方便点