iOS & XML浅谈

XML简介

  • XML出身名门,W3C制定,微软和IBM曾经共同大力推荐过的数据格式.
  • XML指可扩展标记语言(Extensible Markup Language).
  • 被设计用来传输和存储数据.在以前是常用的数据格式.
  • 对比 : HTML超文本标记语言,是设计用来显示网页的.并不是保存数据和传输的方式.

XML格式

  • XML由标记组成,xml的标记是可扩展的,没有预定义.
  • <tag>这就是标记.有开始标记和结束标记.例如 <tag>表示开始标记,</tag>表示结束标记.是成对出现的.
  • 注意 : 一个XML文件有且只有一个节点(标签).

XML演练

  • 新建一个empty空文件,取名demo.xml
<!-- 描述几个学生 -->
<Class>

<!-- 用name和age标签描述学生 -->
    <student>
        <name>卡特琳娜</name>
        <age>21</age>
    </student>

    <student>
        <name>牛头</name>
        <age>22</age>
    </student>

<!-- 标签中定义属性也可以描述学生 -->
    <student name="卡尔萨斯" age="23">
    </studet>

<!-- 标签中定义属性描述学生的简写 -->
    <student name="瑞兹" age="24" />

</Class>

XML文件解析方式

  • 如果想获取到XML文件中保存的数据,需要将XML文件中的数据解析出来.
  • SAX方式解析和DOM方式解析

SAX方式解析XML

  • 苹果官方提供的原生的解析XML文件的方式
  • 在iOS上解析XML文件的方式.
  • 速度快,内存占用小.
  • 是只读的,只能读取XML文件数据不能做修改操作.

DOM方式解析XML

  • 是在MAC使用的解析方式.
  • 注意 : 内存消耗极大,不适用于手机.
  • 可以读写XML文件.
  • iPhone无法直接使用DOM方式解析XML.
  • 如果要在iPhone上使用DOM方式解析XML文件,可以使用第三方框架.

XML解析之SAX解析

SAX方式解析XML步骤

  • 创建XML文件解析器.
  • 遵守解析器的代理协议.
  • 启动解析器,开始解析.

XML文件的解析是在代理方法中完成的.需要实现以下代理方法.

1.开始解析`XML`文件.
2.找开始节点(标签).
3.找节点之间的内容.
4.找结束节点.
5.结束解析`XML`文件.
6.监听`XML`文件是否解析出错.

SAX方式解析XML实现

  • 1.发送网络请求,获取二进制的XML数据.
/// 下载二进制的XML数据
- (void)downloadXML
{
    // 获取URL
    NSURL *url = [NSURL URLWithString:@"http://localhost/videos.xml"];
    // 创建请求
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    // 发送异步请求,请求自建服务器中的demo.json数据
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {

        // 判断链接是否错误
        if (connectionError) {
            NSLog(@"链接错误 %@",connectionError);
            return;
        }

        // 检查响应体是否有错
        NSHTTPURLResponse *HTTPURLResponse = (NSHTTPURLResponse *)response;
        if (HTTPURLResponse.statusCode == 200 || HTTPURLResponse.statusCode == 304) {

        NSLog(@"二进制的XML数据 %@",data);

        } else {
            NSLog(@"服务器内部错误");
            return;
        }
    }];
}

  • 2.拿到二进制的XML数据之后,创建XML解析器,设置代理,启动解析器.
// 创建XML解析器
NSXMLParser *XMLParser = [[NSXMLParser alloc] initWithData:data];
// 设置解析器代理协议
XMLParser.delegate = self;
// 开始解析
[XMLParser parse];

  • 3.实现代理方法
// 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);
}

// 3.找节点之间的内容
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    // string : 节点之间的内容.
    NSLog(@"3.找节点之间的内容 %@",string);
}

// 4.找结束节点
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    NSLog(@"4.找结束节点 %@",elementName);
}

// 5.结束解析文档
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
    NSLog(@"5.结束解析文档");
}

// 6.监听解析是否出错
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
    NSLog(@"6.监听解析是否出错 %@",parseError);
}

结论

  • 通过代理方法中的NSLog,可以了解到解析XML文件中数据的过程.
  • 以上步骤,2--->3--->4,会不断循环,一直到所有数据解析完成.
  • 节点之间的内容并不是一次就能找全的,有时候需要循环多次才能找全.
  • 如果解析数据量非常大的XML文件是很耗时的.当在哪个线程设置了代理协议,就在哪个线程中解析.

XML解析之转模型

准备模型类

  • .h文件中的声明
@interface VideoModel : NSObject

/// 视频编号
@property (nonatomic,copy) NSString *videoId;
/// 视频名称
@property (nonatomic,copy) NSString *name;
/// 视频长度
@property (nonatomic,copy) NSString *length;
/// 视频链接
@property (nonatomic,copy) NSString *videoURL;
/// 视频图标
@property (nonatomic,copy) NSString *imageURL;
/// 视频标题
@property (nonatomic,copy) NSString *desc;
/// 视频作者
@property (nonatomic,copy) NSString *teacher;

/// 字典转模型 : 解析XML的时候,没有字典,所以这个不需要
//+ (instancetype)videoWithDict:(NSDictionary *)dict;

@end

/*
 <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>
 */

  • .m文件中的实现
@implementation VideoModel

/// 无用处
+ (instancetype)videoWithDict:(NSDictionary *)dict
{
    // 创建模型对象
    VideoModel *v = [[VideoModel alloc] init];

    // KVC 字典转模型
    [v setValuesForKeysWithDictionary:dict];

    // 返回模型对象
    return v;
}

/// 打印模型的详细内容
- (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

解析XML文件

XML文件中的数据读取出来,转换成可以直接使用的模型数据.

  • XML数据转模型数据的准备工作
@interface ViewController () <NSXMLParserDelegate>

/// video对应的模型对象
@property (nonatomic,strong) VideoModel *currentVideo;
/// 拼接节点之间内容的字符串
@property (nonatomic,copy) NSMutableString *stringM;
/// 模型数组
@property (nonatomic,strong) NSMutableArray *videoM;

@end

@implementation ViewController

- (NSMutableString *)stringM
{
    if (_stringM == nil) {
        _stringM = [NSMutableString string];
    }
    return _stringM;
}

- (NSMutableArray *)videoM
{
    if (_videoM == nil) {
        _videoM = [NSMutableArray array];
    }
    return _videoM;
}

实现代理方法

  • 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节点(标签),就创建一个对应的模型对象
    if ([elementName isEqualToString:@"video"]) {
        // 创建video标签对应的模型对象
        self.currentVideo = [[VideoModel alloc] init];
        // 拿到标签的属性,可以给模型的videoId属性赋值
        self.currentVideo.videoId = attributeDict[@"videoId"];
        // 将模型对象添加到模型数组中
        [self.videoM addObject:self.currentVideo];
    }
}

  • 3.找节点之间的内容
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    // string : 节点之间的内容.(内容不会一次性找全)
    NSLog(@"3.找节点之间的内容 %@",string);

    // 拼接节点之间的字符串
    [self.stringM appendString:string];
}

  • 4.找结束节点

  • 注意 : 在KVC给模型的属性赋值的过程中,一定要排除videosvideo两个节点.因为这两个节点不需要转模型,且在模型类中没有对应的属性值.不排除掉,KVC在赋值过程中程序会崩溃.

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    NSLog(@"4.找结束节点 %@",elementName);

    // 找到了结束节点,说明节点之间的内容已经获取到了,就可以给模型的属性赋值了
    // 如果要在这个方法里面获取到模型,那么模型需要定义成全局的
    // 一定要排除`videos`和`video`两个节点,这两个节点不需要转模型
    if (![elementName isEqualToString:@"videos"]&&![elementName isEqualToString:@"video"]) {
        [self.currentVideo setValue:self.stringM forKey:elementName];
    }
}

  • 5.结束解析文档
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
    NSLog(@"5.结束解析文档");

    // 打印全部模型
    NSLog(@"%@",self.videoM);
}

  • 6.监听解析是否出错
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
    NSLog(@"6.监听解析是否出错 %@",parseError);
}

结论

  • 不能按照字典转模型的逻辑来处理XML转模型.解析XML数据的过程中没有字典.

  • 每个video标签就是描述一个视频的.每个模型对象也是用来描述一个视频的.所以每找到一个video标签就要创建一个对应的模型对象.模型对象属性赋值会在多个代理方法中进行赋值.所以需要定义成全局的模型对象.

  • 节点之间的内容有时候不会一次性就找全,所以需要多次拼接字符串,而且要在找结束节点代理方法中使用,所以也要定义成全局的可变字符串.

  • 为了能够打印出所有的模型对象,需要将模型对象添加到数组中.


清空供拼接的可变字符串

// 4.找结束节点
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    NSLog(@"4.找结束节点 %@",elementName);

    // 找到了结束节点,说明节点之间的内容已经获取到了,就可以给模型的属性赋值了
    // 如果要在这个方法里面获取到模型,那么模型需要定义成全局的
    // 一定要排除`videos`和`video`两个节点,这两个节点不需要转模型
    if (![elementName isEqualToString:@"videos"]&&![elementName isEqualToString:@"video"]) {
        [self.currentVideo setValue:self.stringM forKey:elementName];
    }

    // 给模型属性赋值完成之后,需要清空字符串.
    [self.stringM setString:@""];
}



XML解析之DOM解析

  • 仅做了解.

DOM简介

  • DOM : Document Object Model
  • DOM : 文档对象模型 (文档模型)
  • 一次性把XML文件全部加载到内存,内存消耗大,适合读写比较小的XML文件.
  • 之前的SAX方式只能读取XML文件,但是DOM方式可以修改XML文件,包括添加删除修改节点.
  • iOS系统默认不支持DOM解析,在iOS系统下解析DOM的话需要第三方框架.
  • 第三方框架 : GData/KissXML(XMPP中使用此框架)

GData中类的关系分析

集成GData框架

  • 导入GDataXMLNode第三方文件.
  • 解决GData的报错.


GData解析XML的步骤

1.获取XML文档.
2.获取XML文档的根标签.
3.遍历根标签,获取根标签的子标签(video标签),创建video标签对应的模型对象.
4.遍历video标签,获取video标签的子标签,给模型对象的属性赋值.
5.遍历video标签,获取video标签的属性,给模型对象的videoId属性赋值.

GData解析XML的实现

  • 模型类没有变化,还是SAX方式解析的那个模型类.
  • 从网络中获取到XML文件的二进制数据.
  • 代码实现 :
/// DOM方式解析XML
- (void)DOM:(NSData *)data
{
    // 1.获取XML文档
    GDataXMLDocument *XMLDocument = [[GDataXMLDocument alloc] initWithData:data error:NULL];

    // 2.获取XML文档的根标签
    GDataXMLElement *rootElement = XMLDocument.rootElement;

    // 创建模型数组,将模型对象添加到模型数组中
    NSMutableArray *videoM = [NSMutableArray array];

    // 3.遍历根标签,获取根标签的子标签(video标签),创建video标签对应的模型对象
    for (GDataXMLElement *videoElement in rootElement.children) {

        // 创建video标签对应的模型对象
        VideoModel *video = [[VideoModel alloc] init];
        // 将模型对象添加到模型数组中
        [videoM addObject:video];

        // 4.遍历video标签,获取video标签的子标签,给模型对象的属性赋值
        for (GDataXMLElement *subElement in videoElement.children) {
            // KVC给模型的属性赋值
            [video setValue:subElement.stringValue forKey:subElement.name];
        }

        // 5.遍历video标签,获取video标签的属性,给模型对象的videoId属性赋值
        NSLog(@"%@",[videoElement.attributes class]);
        for (GDataXMLNode *attr in videoElement.attributes) {
            // KVC给模型的videoId属性赋值
            [video setValue:attr.stringValue forKey:attr.name];
        }
    }

    NSLog(@"%@",videoM);
}






























阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hanxujie00/article/details/52139094
文章标签: xml iOS 数据
个人分类: iOS学习笔记
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭