XML文件解析方式
-
SAX方式解析XML
苹果官方提供的原生的解析XML文件的方式
在iOS上解析XML文件的方式.
速度快,内存占用小.
是只读的,只能读取XML文件数据不能做修改操作. -
DOM方式解析XML
是在MAC使用的解析方式.
注意 : 内存消耗极大,不适用于手机.
可以读写XML文件.
iPhone无法直接使用DOM方式解析XML.
如果要在iPhone上使用DOM方式解析XML文件,可以使用第三方框架.
解析XML过程
//发送请求
1.获取URL–>创建请求–>发送请求–>响应处理–>XML解析
//xml解析器
2>创建XML解析器–>设置解析器代理–>开启解析器
//代理方法
3>开启–>开始节点–>节点内容–>结束节点–>结束–>监听错误信息
//模型(开始节点–节点内容–结束节点)
4>判断开始节点–创建模型–标签属性赋值给模型–模型添加到数组 //可遍数组
5>保存节点内容的可变字符串 //可变字符串
6>每次取出属性数组的模型–判断对应节点–给模型属性赋值–可变字符串置为@“”
NSXMLParser–parse–DidStartDocument–didStartElement–foundCharacters–didEndElement–DidEndDocument–parseErrorOccurred
实例XML解析
//
// ViewController.m
// XML解析之SAX
//
// Created by Apple on 14/7/5.
// Copyright © 2014年 mac. All rights reserved.
//
#import "ViewController.h"
#import "VideoModel.h"
@interface ViewController () <NSXMLParserDelegate>
@property (nonatomic, strong) NSMutableArray *videoM;
@property (nonatomic, copy) NSMutableString *stringM;
@property (nonatomic, strong) VideoModel *currentModel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self loadXML];
}
- (void)loadXML {
// URL
NSURL *URL= [NSURL URLWithString:@"http://localhost/videos.xml"];
// 请求
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
// 发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
// 处理响应
if (connectionError == nil && data != nil) {
// 反序列化 : 解析XML文件的二进制
// 1.创建XML解析器
NSXMLParser *XMLParser = [[NSXMLParser alloc] initWithData:data];
// 2.设置xml解析器的代理
XMLParser.delegate = self;
// 3.启动解析器
[XMLParser parse];
} else {
NSLog(@"%@",connectionError);
}
}];
}
#pragma mark - NSXMLParserDelegate
/*
1.VideoModel 什么时候创建?
2.模型对象的videoId属性什么时候赋值?
3.什么时候把创建的所有的模型对象添加到数组中?
4.什么时候给模型对象的其他属性赋值?
5.模型对象属性赋值的值在哪里?
*/
// 1.开始解析XML文档
- (void)parserDidStartDocument:(NSXMLParser *)parser {
NSLog(@"1.开始解析XML文档");
}
// 2.找开始节点/标签/元素
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName attributes:(NSDictionary<NSString *, NSString *> *)attributeDict {
// elementName : 开始节点
// attributeDict : 节点的属性
// NSLog(@"2.找开始节点 = %@--%@",elementName,attributeDict);
// 一旦找到video节点就需要建立一个模型对象
if ([elementName isEqualToString:@"video"]) {
// 创建video节点对应的模型对象
VideoModel *model = [[VideoModel alloc] init];
// 给模型对象的videoId属性赋值 : 因为只有在这里可以拿到videoId(节点属性)
model.videoId = attributeDict[@"videoId"];
// 把创建好的模型对象添加到模型数组 : 简单,在哪里创建的就在哪里添加
[self.videoM addObject:model];
}
}
// 3.找节点之间的类容
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
// string : 节点之间的内容
// NSLog(@"3.找节点之间的类容 = %@",string);
// 循环拼接节点之间的内容
[self.stringM appendString:string];
}
// 4.找结束节点
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName {
// elementName : 结束节点
// NSLog(@"4.找结束节点 = %@",elementName);
// 在找到结束节点之后,给模型对象的其他属性赋值
// 从数组里面取最后一个元素(模型对象)
VideoModel *model = [self.videoM lastObject];
// 给模型对象的其他属性赋值,但是videos节点和video节点是不需要赋值的
if ([elementName isEqualToString:@"videos"] || [elementName isEqualToString:@"video"]) {
return;
}
// 只有排除了videos节点和video节点,才需要赋值
[model setValue:self.stringM forKey:elementName];
// 使用完了之后要把可变字符串清空,以免再次拼接时,把前面的内容也拼接进去了
// 这是把不可变字符串赋值给可变字符串,不被允许的
// self.stringM = @"";
// 清空之后,stringM下次再用的时候,又要懒加载创建,性能差了一点
// self.stringM = nil;
[self.stringM setString:@""];
}
// 5.结束解析文档
- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSLog(@"5.结束解析文档");
NSLog(@"%@",self.videoM);
}
// 6.监听XML文件是否解析出错
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSLog(@"6.监听XML文件是否解析出错 = %@",parseError);
}
- (NSMutableArray *)videoM {
if (_videoM == nil) {
_videoM = [NSMutableArray array];
}
return _videoM;
}
- (NSMutableString *)stringM {
if (_stringM == nil) {
_stringM = [NSMutableString string];
}
return _stringM;
}
/*
全局的模型对象不能懒加载,如果懒加载之后,当前类有且只有一个model
*/
//- (VideoModel *)currentModel
//{
// if (_currentModel == nil) {
// _currentModel = [[VideoModel alloc] init];
// }
// return _currentModel;
//}
@end
-
通过代理方法中的NSLog,可以了解到解析XML文件中数据的过程.
-
以上步骤,2—>3—>4,会不断循环,一直到所有数据解析完成.
-
节点之间的内容并不是一次就能找全的,有时候需要循环多次才能找全.
-
如果解析数据量非常大的XML文件是很耗时的.当在哪个线程设置了代理协议,就在哪个线程中解析.
-
不能按照字典转模型的逻辑来处理XML转模型.解析XML数据的过程中没有字典.
-
每个video标签就是描述一个视频的.每个模型对象也是用来描述一个视频的.所以每找到一个video标签就要创建一个对应的模型对象.模型对象属性赋值会在多个代理方法中进行赋值.所以需要定义成全局的模型对象.
-
节点之间的内容有时候不会一次性就找全,所以需要多次拼接字符串,而且要在找结束节点代理方法中使用,所以也要定义成全局的可变字符串.
-
为了能够打印出所有的模型对象,需要将模型对象添加到数组中.
-
在拼接下一个节点之间的内容之前,给模型属性付完值之后,将供拼接的可变字符串清空.
注意 : 多次拼接完节点之间的内容,且在找结束节点代理方法中,给模型属性赋值完成之后,需要清空这个用于拼接的可变字符串.否则,会c重复拼接字符串,即将上一个节点之间的内容继续拼接到下一个节点之间的内容前面.