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"

(2)在工程的Build phases设置的Link Binary With Libraries里面点击加号,搜索libxml2.tbd,选择添加

(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解析方便点

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容