iOS个人整理29-JSON与xml文件解析(dom/SAX解析)

一、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/libxm2,这里三个反斜杠不能少

这时候进行编译,可能会出现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];
        [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解析方便点

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值