美文网首页IOS专辑iOS 开发 将来跳槽用
iOS-进阶整理04 - JSON与xml文件解析

iOS-进阶整理04 - JSON与xml文件解析

作者: 简单也好 | 来源:发表于2016-04-15 17:00 被阅读205次

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

    相关文章

      网友评论

        本文标题:iOS-进阶整理04 - JSON与xml文件解析

        本文链接:https://www.haomeiwen.com/subject/mywwlttx.html