iOS 中 XML 解析 NSXMLParse和GDataXML

作者: Aaronn | 来源:发表于2016-12-08 21:52 被阅读0次

    XML解析

    • 可扩展标记语言
    • XML的特点,出身名门,W3C制定,微软和IBM曾经共同大力推荐过的数据格式
    • XML 指可扩展标记语言(eXtensible Markup Language)
      • 被设计用来传输和存储数据
      • HTML 是设计用来表示页面的

    SAX解析

    SAX是iOS默认的解析XML的方式,simple API for XML . 是一种占用内存非常低,但是只能读取不能写入的解析方式.因为他是一行一行的解析的.

    • 准备一个模型以及两个属性接收解析出来的数据.
    #import "ViewController.h"
    //导入模型类
    #import "VideoModel.h"
    
    //遵守代理协议
    @interface ViewController ()<NSXMLParserDelegate>
    
    //用于保存模型的数组
    @property(nonatomic,strong)NSMutableArray <VideoModel *>*modelArr;
    
    //用于临时保存解析出来的数据.
    @property(nonatomic,strong)NSMutableString *mStr;
    
    @end
    
    • NSXMLParse 类进行解析. 主要通过实现对象代理方法来解析.比较复杂.
    @implementation ViewController
    
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //URL 加载本地Apache服务器的数据解析
        NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos.xml"];
        
        //通过Session自动开启线程进行异步任务.
        [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            //创建xml解析器
            NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
            //设置代理
            parser.delegate = self;
            //解析开始
            [parser parse];
        }] resume];
        
    }
    
    //初始化可变数组
    - (NSMutableArray *)modelArr
    {
        if (_modelArr == nil) {
            _modelArr = [NSMutableArray array];
        }
        return _modelArr;
    }
    
    //初始化可变字符串
    - (NSMutableString *)mStr
    {
        if (_mStr == nil) {
            _mStr = [NSMutableString string];
        }
        return _mStr;
    }
    
    • 真正用于解析的代理方法 只用这5个,前后两对方法,加上中间一个获取数据的方法.
    /**
     开始解析
     */
    - (void)parserDidStartDocument:(NSXMLParser *)parser;
    {
        //这里只是开始,貌似不用做什么
    }
    
    /**
     开始一个新标签,这个时候应该创建对应的模型对象或者准备为模型的属性赋值.
     @param parser 解析器
     @param elementName 标签元素名字
     @param attributeDict 标签的属性
     */
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName attributes:(NSDictionary<NSString *, NSString *> *)attributeDict;
    {
        //看一下xml的结构,决定这里是干什么.对应当前的xml应该是判断后创建模型对象
        /*
         <videos>
         <video videoId="1">
         <name>01.C语言-语法预览</name>
         <length>320</length>
         <videoURL>/itcast/videos/01.C语言-语法预览.mp4</videoURL>
         <imageURL>/itcast/images/head1.png</imageURL>
         <desc>C语言-语法预览</desc>
         <teacher>李雷</teacher>
         </video>
         */
        if ([elementName isEqualToString:@"video"]) {
            //创建新的模型对象
            VideoModel *model = [VideoModel new];
            //取出属性,为videoId赋值
            model.videoId = @(attributeDict[@"videoId"].intValue);
            //将模型保存到数组
            [self.modelArr addObject:model];
        }
        
    }
    
    
    
    /**
     解析到标签中间的文字 标签中的文字不是一次性能读完的,可能会分几次调用这个方法,所以创建一个可变字符串保存起来.
    
     @param parser 解析器
     @param string 文字
     */
    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
    {
        [self.mStr appendString:string];
    }
    
    
    
    /**
     解析到一个元素结束的地方.
    
     @param parser 解析器
     @param elementName 元素名字
     */
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName;
    {
        //进行判断,外层标签不进行KVC,否则崩溃.如果标签过多也可以重写model的方法 里面什么都不做就可以避免KVC报错
        //- (void)setValue:(id)value forUndefinedKey:(NSString *)key
        if (![elementName isEqualToString:@"video"] && ![elementName isEqualToString:@"videos"]) {
            //获取当前model
            VideoModel *model = self.modelArr.lastObject;
            //属性的值就是解析出来的string,key则是标签的名字
            [model setValue:self.mStr forKey:elementName];
        }
        //最后对mStr进行清空,准备进行下一个标签的解析
        self.mStr.string = @"";
    }
    /**
     结束解析
     */
    - (void)parserDidEndDocument:(NSXMLParser *)parser;
    {
        //所有标签解析完毕,打印数组看看是否转换成功.
        NSLog(@"%@",self.modelArr);
    }
    
    @end
    
    
    • 当然写完以后一定记得封装到对应的模型中,创建模型方法.那么在控制器中一句代码就搞定了.
    - (NSArray *)parserXML:(NSString *)URLString;
    {
        //URL
        NSURL *url = [NSURL URLWithString:URLString];   
        //通过Session自动开启线程进行异步任务.   
        [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            //创建xml解析器
            NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
            //设置代理
            parser.delegate = self;
            //解析开始
            [parser parse];
        }] resume];
        
        return self.modelArr;
    }
    
    控制器一句话搞定加载

    DOM解析

    因为iOS不能直接使用MAC的解析方式,所以DOM解析使用第三方框架.GDataXMLNode
    它有增加删除等方法,头文件里面有对应的方法,这里我们仅使用它来进行XML的反序列化. 也就是解析

    如果你不是通过控制台中pod加载的框架

    • pod init
    • pod GDataXML-HTML
    • pod install

    那么你可能会碰到引入框架后#import <libxml/tree.h>报错的问题.

    按照注释,在project->build Settings ->Header Search PathsOther Linker Flags 中分别添加两个地址

    1. /usr/include/libxml2
    2. -lxml2
    • 原文注释
    // libxml includes require that the target Header Search Paths contain
    //
    //   /usr/include/libxml2
    //
    // and Other Linker Flags contain
    //
    //   -lxml2
    
    使用pod install 导入框架会自动进行配置,非常方便

    DOM的解析有点类似于字典转模型的过程.根据解析的XML的结构不同嵌套层次也不同.
    由于整个解析过程比较连贯,所以直接复制粘贴整段代码

    • 下面是全部代码,注释非常详细.
    #import "VideoModel.h"
    
    #import <GDataXMLNode.h>
    
    @interface ViewController ()
    
    
    @end
    
    @implementation ViewController
    
    
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        
        //1. 通过URL获取XML的Data数据.
        NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos.xml"];
        
        [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            
            //2. 获取到Data数据,创建GData对象 这里创建方法接收的是一个XML的string,所以先转换Data成String
            NSString *xmlString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            
            //这个方法可以进去看看实现,默认是用UTF-8编码
            GDataXMLDocument *GD = [[GDataXMLDocument alloc]initWithXMLString:xmlString error:nil];
            
            //3. 创建对象以后,取出根元素的子元素.返回的数组中都是GDataXMLElement
            NSArray *rootArr = GD.rootElement.children;
            
            //4. 那么现在要做的就是遍历这个数组,对每一个元素进行操作,转换成模型了,这个过程类似于字典转模型.
            
            //创建一个可变数组保存转换好的模型
            NSMutableArray *modelArrM = [NSMutableArray array];
            for (GDataXMLElement *element in rootArr) {
                
                
                // 1. 取出数组中的每一个元素后,先将这一组的video标签的属性videoId取出来
                /*
                 1. 属性返回的是一个数组,我们的属性只有一个,所以取出第一个.元素类型是GDataXMLNode
                 2. 通过这个结构我们不难发现,elment对应的是一个树枝节点,它包含attributes数组,`属性`一定是到头了,是叶子节点.所以这个数组中存的是多个GDataXMLNode类型的元素.
                 3. GDataXMLNode是GDataXMLElement的父类.node有对应的方法name,stringValue.返回键值对.
                 */
                GDataXMLNode *node = element.attributes.firstObject;
                //NSLog(@"name = %@ value = %@",node.name,node.stringValue);
                
                //2. 创建一个模型将video标签的属性保存 KVC
                VideoModel *model = [VideoModel new];
                [model setValue:node.stringValue forKey:node.name];
                
                //3. 取出video标签的子标签,数组,每个子元素依然是GDataXMLElement 代表着一个一个的标签.
                //NSLog(@"element.children = %@",[element.children.firstObject class]);
                
                //循环遍历数组
                for (GDataXMLElement *elementTag in element.children) {
                    //4. 这里的每一个tag就是最后的叶子借点了. name 是 key xml是value
                    //NSLog(@"%@",elementTag);
                    [model setValue:elementTag.XMLString forKey:elementTag.name];
                    
                }
                
                //5. 将添加完元素的模型保存到数组
                [modelArrM addObject:model];
                
            }
            
            //转换完毕,看看结果
            NSLog(@"%@",modelArrM);
            
        }] resume];
          
    }
    @end
    
    
    转换完毕.看看结果

    相关文章

      网友评论

        本文标题:iOS 中 XML 解析 NSXMLParse和GDataXML

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