zoukankan      html  css  js  c++  java
  • iOS高级编程之JSON,XML数据解析

    解析的基本概念

    所谓“解析”:从事先规定好的格式串中提取数据

    解析的前提:提前约定好格式、数据提供方按照格式提供数据、数据获取方按照格式获取数据

    iOS开发常见的解析:XML解析、JSON解析

    一、JSON数据结构

     

    JSON数据结构基本概念

    Javascript Object Notation ,轻量级的数据交换格式,采用完全独立于语言的文本格式,被称为理想的数据交换语言,易于人阅读和便携,同时也易于及其解析何程,由于JSON解析便捷、快速,并且相同数据用JSON编辑所占的内存更小,所以在iOS中我们我们使用JSON解析更加普遍。

     

    JSON文档有两种结构:对象 、数据

    对象:以“{“开始,以”}”结束,是“名称/值”对儿的集合。名称和值中间用“:”隔开。多个“名称/值”对之间用“,”隔开。类似OC中的字典。

    数组:以“["开始,以“]”结束,中间是数据。数据以“,”分割。

    JSON中的数据类型:字符串、数值、BOOL、对象、数组。 

     例如:

     

     

    JSON – OC 转换对照表

     

     JSON数据结构的功能

    1、数据交换

    2、内容管理

    3、配置文件

     

     

    JSON解析方案

     

    在iOS中,JSON的常见解析方案有4种 
    第三方框架:JSONKit、SBJson、TouchJSON(性能从左到右,越差)  JSONKit从2012年开始就没有更新了
    苹果原生(自带):NSJSONSerialization(性能最好)

    JSONObjectWithData常见的语法

    从JSON数据到OC对象

    + (nullable id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;

    从OC对象到JSON数据

    + (nullable NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error;

    下面是NSJSONSerialization解析过程:

    如何进行XML解析

    有以下两种方法:

    1、SAX解析

    SAX:Simple API for XML .基于事件驱动的解析方式,逐行解析数据。(采用协议回调机制)

    SAX解析XML,是基于事件通知的模式,一边读取XML文档一边处理,不必等整个文档加载完之后才采取操作,SAX解析器会检测整个XML树形结构,你的代码会控制它在哪里停止,使用哪些数据之类的事情。就是说,SAX可控制性强,占用内存小,适用于提取部分数据。当在读取解析过程中遇到需要处理的对象,会发出通知对其进行处理,如果XML格式在某一处出现错误,前面的数据会被提取出来,错误后面数据的就显示不出来。

     

    NSXMLParse类是iOS自带的XML解析类。采用SAX方式解析数据

    解析过程由NSXMLParserDelegate协议方法回调

    解析过程:开始标签->取值->结束标签->取值

    代码的实现过程是,首先我们建立一个XML文档,并引入文件,如下: 

    <!-- 我是注释 -->
    <Class className="三年一班">
        <Student name="zs" color="yellow" />
        <Student name="ls" color="yellow" />
        <aa>
            <bb></bb>
        </aa>
    </Class>

    首先创建模型

    .h中

    #import <Foundation/Foundation.h>
    
    @interface Video : NSObject
    
    @property (nonatomic, copy) NSNumber *videoId;
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, copy) NSNumber *length;//值不改变
    @property (nonatomic, copy) NSString *videoURL;
    @property (nonatomic, copy) NSString *imageURL;
    @property (nonatomic, copy) NSString *desc;
    @property (nonatomic, copy) NSString *teacher;
    
    @property (nonatomic, readonly) NSString *time;
    
    - (instancetype)initWithDict:(NSDictionary *)dict;
    + (instancetype)videoWithDict:(NSDictionary *)dict;
    
    @end

    .m中

    #import "Video.h"
    
    @implementation Video
    
    - (instancetype)initWithDict:(NSDictionary *)dict {
        self = [super init];
        if (self) {
            [self setValuesForKeysWithDictionary:dict];
        }
        return self;
    }
    
    + (instancetype)videoWithDict:(NSDictionary *)dict {
        return [[self alloc] initWithDict:dict];
    }
    
    - (NSString *)time {
        int len = self.length.intValue;
        
        return [NSString stringWithFormat:@"%02d:%02d:%02d", len / 3600, (len % 3600) / 60, (len % 60)];
    }
    
    - (NSString *)description {
        return [NSString stringWithFormat:@"<%@ : %p> { videoId : %@, name : %@, length : %@, videoURL : %@, imageURL : %@, desc : %@, teacher : %@}", [self class], self, self.videoId, self.name, self.length, self.videoURL, self.imageURL, self.desc, self.teacher];
    }
    
    @end

    在viewController.m中

    #import "ViewController.h"
    #import "Video.h"
    @interface ViewController () <NSXMLParserDelegate>
    @property (nonatomic, strong) NSMutableArray *videos;
    //当前创建的video对象
    @property (nonatomic, strong) Video *currentVideo;
    //存储当前节点的内容
    @property (nonatomic, copy) NSMutableString *mString;
    @end
    
    @implementation ViewController
    //懒加载
    - (NSMutableArray *)videos {
        if (_videos == nil) {
            _videos = [NSMutableArray arrayWithCapacity:10];
        }
        return _videos;
    }
    
    - (NSMutableString *)mString {
        if (_mString == nil) {
            _mString = [NSMutableString string];
        }
        return _mString;
    }
    
    
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        [self loadXML];
    
    }
    
    //异步请求xml
    - (void)loadXML {
        //异步请求服务器的xml文件
        NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos.xml"];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
            if (connectionError) {
                NSLog(@"连接错误 %@",connectionError);
                return;
            }
            
            //
            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
            if (httpResponse.statusCode == 200 || httpResponse.statusCode == 304) {
                //解析数据
                NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
                //设置代理
                parser.delegate = self;
                //开始执行代理的方法,代理的方法中开始解析的
                
                [parser parse];
            }else{
                NSLog(@"服务器内部错误");
            }
        }];
    }
    
    //代理方法执行  和 设置代理属性在同一个线程
    //代理的方法
    //1 开始解析文档
    - (void)parserDidStartDocument:(NSXMLParser *)parser {
        NSLog(@"1 开始解析文档  %@",[NSThread currentThread]);
    }
    //2 找开始节点
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict {
        //elementName 节点的名称
        //attributeDict  标签的属性
        NSLog(@"2 找开始节点  %@--%@",elementName,attributeDict);
        
        //如果是video标签,创建video对象
        if ([elementName isEqualToString:@"video"]) {
            self.currentVideo = [[Video alloc] init];
            self.currentVideo.videoId = @([attributeDict[@"videoId"] intValue]);
            
            //添加到数组中
            [self.videos addObject:self.currentVideo];
            
            
        }
        
        
    }
    
    //3 找节点之间的内容
    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
        //
        NSLog(@"3 找节点之间的内容 %@",string);
        //拼接字符串
        [self.mString appendString:string];
        
    }
    
    //4 找结束节点
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
        //elementName 节点名称
        NSLog(@"4 找结束节点 %@",elementName);
        
        //判断标签是否是对应的属性
        if ([elementName isEqualToString:@"name"]) {
            self.currentVideo.name = self.mString;
        }else if([elementName isEqualToString:@"length"]) {
            self.currentVideo.length = @(self.mString.intValue);
        }else if([elementName isEqualToString:@"videoURL"]) {
            self.currentVideo.videoURL = self.mString;
        }else if([elementName isEqualToString:@"imageURL"]) {
            self.currentVideo.imageURL = self.mString;
        }else if([elementName isEqualToString:@"desc"]) {
            self.currentVideo.desc = self.mString;
        }else if([elementName isEqualToString:@"teacher"]) {
            self.currentVideo.teacher = self.mString;
        }
        //清空可变字符串
        [self.mString setString:@""];
    }
    
    //5 结束解析文档
    - (void)parserDidEndDocument:(NSXMLParser *)parser {
        NSLog(@"5 结束解析文档");
        NSLog(@"%@",self.videos);
    }
    //6 解析出错
    - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
        NSLog(@"出错");
    }
    
    @end

    用KVC的方式

    //4 找结束节点
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
        //elementName 节点名称
        NSLog(@"4 找结束节点 %@",elementName);
        
        //判断标签是否是对应的属性
        if (![elementName isEqualToString:@"video"] && ![elementName isEqualToString:@"videos"]) {
            //self.currentVideo.length =
            //kvc 赋值的过程就是地址指向的过程,不会做类型转换!!!其实是set方法!!!
            [self.currentVideo setValue:self.mString forKey:elementName];
            
        }
        
        //清空可变字符串
        [self.mString setString:@""];
    }

    2、DOM工具解析

    DOM:Document Object Model (文档对象模型)。解析时需要将XML文件整体读入,并且将XML结构化成树状,使用时再通过树状结构读取相关数据,查找特定节点,然后对节点进行读或写。他的主要优势是实现简单,读写平衡;缺点是比较占内存,因为他要把整个xml文档都读入内存,文件越大,这种缺点就越明显。当文件内容出现错误时,在输入框内会标记出错误的位置

    GDataXMLNode是Google提供的开元XML解析类,对libxml2.dylib进行了Objective-C的封装,因此在使用GDataXML之前,你需要先导入libxml2

    iOS中包含一个C语言的动态链接库libxml2.dylib,解析速度比NSXMLParser快

     

    模型

    #import <Foundation/Foundation.h>
    
    @interface Video : NSObject
    
    @property (nonatomic, copy) NSNumber *videoId;
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, copy) NSNumber *length;
    @property (nonatomic, copy) NSString *videoURL;
    @property (nonatomic, copy) NSString *imageURL;
    @property (nonatomic, copy) NSString *desc;
    @property (nonatomic, copy) NSString *teacher;
    
    @property (nonatomic, readonly) NSString *time;
    
    - (instancetype)initWithDict:(NSDictionary *)dict;
    + (instancetype)videoWithDict:(NSDictionary *)dict;
    
    @end
    #import "Video.h"
    
    @implementation Video
    
    - (instancetype)initWithDict:(NSDictionary *)dict {
        self = [super init];
        if (self) {
            [self setValuesForKeysWithDictionary:dict];
        }
        return self;
    }
    
    + (instancetype)videoWithDict:(NSDictionary *)dict {
        return [[self alloc] initWithDict:dict];
    }
    
    - (NSString *)time {
        int len = self.length.intValue;
        
        return [NSString stringWithFormat:@"%02d:%02d:%02d", len / 3600, (len % 3600) / 60, (len % 60)];
    }
    
    - (NSString *)description {
        return [NSString stringWithFormat:@"<%@ : %p> { videoId : %@, name : %@, length : %@, videoURL : %@, imageURL : %@, desc : %@, teacher : %@}", [self class], self, self.videoId, self.name, self.length, self.videoURL, self.imageURL, self.desc, self.teacher];
    }

    在控制器.M文件中

    导入模型头文件  

    导入第三方框架的头文件

    #import "ViewController.h"
    #import "GDataXMLNode.h"
    #import "Video.h"
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    
        [self loadXML];
    }
    
    - (void)loadXML {
        NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos.xml"];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
            if (connectionError) {
                NSLog(@"连接错误 %@",connectionError);
                return;
            }
            
            //
            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
            if (httpResponse.statusCode == 200 || httpResponse.statusCode == 304) {
                //解析数据
                //DOM 文档对象模型
                //1  加载xml文档
                GDataXMLDocument *document = [[GDataXMLDocument alloc] initWithData:data error:NULL];
                
                //获取xml文档的根元素(标签)
                GDataXMLElement *rootElement = document.rootElement;
                
    //            NSLog(@"%@",rootElement);
                
                NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:10];
                //2 遍历所有的video节点
                //video
                for (GDataXMLElement *element in rootElement.children) {
                    //创建对象
                    Video *v = [[Video alloc] init];
                    [mArray addObject:v];
                    
                    //给对象的属性赋值
                    //3 遍历video的子标签,目的是拿到子标签,开始标签和结束标签之间的字符串
                    for (GDataXMLElement *subElement in element.children) {
                        //给属性赋值
                        [v setValue:subElement.stringValue forKey:subElement.name];
                    }
                    //4 遍历video的所有属性
                    
    //                NSLog(@"%@",element.attributes);
                    for (GDataXMLNode *attr in element.attributes) {
                        [v setValue:attr.stringValue forKey:attr.name];
                    }
                    
                    
                }
                NSLog(@"%@",mArray);
                
                
            }else{
                NSLog(@"服务器内部错误");
            }
        }];
  • 相关阅读:
    查看.NET Core源代码通过Autofac实现依赖注入到Controller属性
    序列化二叉树
    把二叉树打印成多行
    按之字形顺序打印二叉树
    对称的二叉树
    JDK源码阅读顺序
    二叉树的下一个结点
    删除链表中重复的结点
    链表中环的入口结点
    字符流中第一个不重复的字符
  • 原文地址:https://www.cnblogs.com/huangfang1314/p/5606789.html
Copyright © 2011-2022 走看看