继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

iOS 中 XML 解析 NSXMLParse和GDataXMLNode的使用

www说
关注TA
已关注
手记 302
粉丝 83
获赞 493

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;
}

https://img2.mukewang.com/5d5e82770001f1af08830629.png

控制器一句话搞定加载

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

https://img.mukewang.com/5d5e827d0001987908920228.png

使用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

https://img3.mukewang.com/5d5e8281000186fd07860404.png

转换完毕.看看结果



作者:Aaronn
链接:https://www.jianshu.com/p/fb34ae5c3177

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP