读写XML文档,目前流行的两种模式:SAX和DOM。
1.SAX是一种基于事件驱动的解析模式。解析XML的时候,程序从上到下读取XML文档,如果遇到开始标签、结束标签、属性等,就会触发相应的事件。
- 优点:解析速度快,iOS重点推荐使用SAX模式解析
- 缺点:只能读取XML文档,不能写入XML文档
2.DOM模式是将XML文档作为一颗树状结构进行分析,提供获取节点的内容,以及相关属性,或是新增、删除和修改节点的内容。XML解析器在加载XML文件以后,DOM将XML文件的元素视为一个树状结构的节点,一次性读入到内存中。
- 优点:能够修改XML文档
- 缺点:如果文档比较大,解析速度就会变慢
NSXML是iOS SDK自带的,也是苹果默认的解析框架,采用SAX模式解析,它是SAX解析模式的代表。本文主要介绍采用NSXML解析XML文件。
采用NSXML解析XML文件
NSXML框架中的核心是NSXMLParser和它的代理NSXMLParserDelegate,NSXMLParserDelegate中的常用方法有:
// 在文档开始的时候触发
- (void)parserDidStartDocument:(NSXMLParser *)parser;
// 在文档出错的时候触发,该方法一般在调试阶段使用,实际发布时意义不大
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError;
// 遇到一个开始标签时触发,其中namespaceURI部分是命名空间,qualifiedName是限定名,attributes是字典类型的属性集合
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict;
// 遇到字符串时触发
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
// 遇到结束标签时触发
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;
// 遇到文档结束时触发
- (void)parserDidEndDocument:(NSXMLParser *)parser;
代码演示解析XML文件
Notes.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<Notes>
<Note id="1">
<CDate>2015-10-20</CDate>
<Content>早上7点钟起床到实验室</Content>
<UserID>xiaolou</UserID>
</Note>
<Note id="2">
<CDate>2015-10-21</CDate>
<Content>学习iOS开发</Content>
<UserID>xiaolou</UserID>
</Note>
<Note id="3">
<CDate>2015-10-22</CDate>
<Content>学习iOS开发之解析XML文件</Content>
<UserID>xiaolou</UserID>
</Note>
<Note id="4">
<CDate>2015-10-23</CDate>
<Content>采用NSXML解析XML文件</Content>
<UserID>xiaolou</UserID>
</Note>
<Note id="5">
<CDate>2015-10-24</CDate>
<Content>解析速度快,iOS重点推荐使用SAX模式解析</Content>
<UserID>xiaolou</UserID>
</Note>
<Note id="6">
<CDate>2015-10-25</CDate>
<Content>该投简历了,希望能找个好工作</Content>
<UserID>xiaolou</UserID>
</Note>
</Notes>
新建一个类NotesXMLParser,继承自NSObject,遵守委托协议NSXMLParserDelegate,并拥有相关属性与方法。
NotesXMLParser.h文件:
#import <Foundation/Foundation.h>
@interface NotesXMLParser : NSObject <NSXMLParserDelegate>
// 解析出的数据内部是字典类型
@property (strong, nonatomic) NSMutableArray *notes;
// 当前标签的名字
@property (strong, nonatomic) NSString *currentTagName;
// 开始解析
- (void)start;
@end
注:定义currentTagName属性的目的是:在触发开始标签方法和结束标签方法 期间临时存储正在解析的元素名,在方法(parser:foundCharacters:)触发时,能够知道目前解析器处于哪个元素之中
NotesXMLParser.m文件中实现start方法:
- (void)start {
NSString *path = [[NSBundle mainBundle] pathForResource:@"Notes" ofType:@"xml"];
NSURL *url = [NSURL fileURLWithPath:path];
// 开始解析XML
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
parser.delegate = self;
[parser parse];
NSLog(@"解析完成。。。");
}
注:NSXMLParser是解析类,它有3个构造方法
1)- (instancetype)initWithContentsOfURL:(NSURL *)url;
2)- (instancetype)initWithData:(NSData *)data;
3) - (instancetype)initWithStream:(NSInputStream *)stream;
实现NSXMLParserDelegate代理方法:
// 文档开始的时候触发
- (void)parserDidStartDocument:(NSXMLParser *)parser {
// 此方法只在解析开始时触发一次,因此可在这个方法中初始化解析过程中用到的一些成员变量
_notes = [NSMutableArray new];
}
// 文档出错的时候触发
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSLog(@"%@", parseError);
}
// 遇到一个开始标签的时候触发
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
// elementName是正在解析的元素的名字
_currentTagName = elementName;
// 如果元素名字为Note,取出它的属性id
if ([_currentTagName isEqualToString:@"Note"]) {
// 属性在attributeDict参数中传递过来,它是一个字典类型,其中的键的名字就是属性的名字,值是属性的值
NSString *_id = [attributeDict objectForKey:@"id"];
NSMutableDictionary *dict = [NSMutableDictionary new];
[dict setObject:_id forKey:@"id"];
[_notes addObject:dict];
}
}
// 遇到字符串时候触发,该方法是解析元素文本内容主要场所
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
// 剔除回车和空格
// stringByTrimmingCharactersInSet:方法是剔除字符方法
// [NSCharacterSet whitespaceAndNewlineCharacterSet]指定字符集为换行符和回车符
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([string isEqualToString:@""]) {
return;
}
NSMutableDictionary *dict = [_notes lastObject];
if ([_currentTagName isEqualToString:@"CDate"] && dict) {
[dict setObject:string forKey:@"CDate"];
}
if ([_currentTagName isEqualToString:@"Content"] && dict) {
[dict setObject:string forKey:@"Content"];
}
if ([_currentTagName isEqualToString:@"UserID"] && dict) {
[dict setObject:string forKey:@"UserID"];
}
}
// 遇到结束标签时触发,在该方法中主要是清理刚刚解析完成的元素产生的影响,以便于不影响接下来的解析
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
// 清理刚才解析的元素的名字,以便于记录接下来解析的元素的名字
self.currentTagName = nil;
}
// 遇到文档结束时触发
- (void)parserDidEndDocument:(NSXMLParser *)parser {
// 使用通知机制将数据通过广播通知投送回表示层
[[NSNotificationCenter defaultCenter] postNotificationName:@"reloadViewNotification" object:self.notes userInfo:nil];
// 解析完成,清理成员变量
self.notes = nil;
}
ViewController.m文件:
#import "ViewController.h"
#import "NotesXMLParser.h"
@interface ViewController () <UITableViewDataSource> {
UITableView *_tableView;
}
//保存数据列表
@property (nonatomic,strong) NSMutableArray* listData;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
_tableView.dataSource = self;
[self.view addSubview:_tableView];
// 注册一个通知,这样ViewController才能在解析完成后接收到投送回来的通知
// 一旦投送成功就会触发reloadView:方法,在该方法中取出数据,并重新加载表示图
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reloadView:)
name:@"reloadViewNotification"
object:nil];
NotesXMLParser *parser = [NotesXMLParser new];
// 开始解析
[parser start];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.listData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ID = @"cell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
NSMutableDictionary* dict = self.listData[indexPath.row];
cell.textLabel.text = [dict objectForKey:@"Content"];
cell.detailTextLabel.text = [dict objectForKey:@"CDate"];
return cell;
}
#pragma mark - 处理通知
- (void)reloadView:(NSNotification *)notification {
// 在该方法中取出数据,并重新加载表示图
NSMutableArray *resList = [notification object];
self.listData = resList;
[_tableView reloadData];
}
@end
后记
小白出手,请多指教。
如言有误,还望斧正!
另:本篇文章内容主要学习自关东升老师的《iOS网络编程与云端应用最佳时间》一书中的内容。
- 转载请保留原文地址:http://www.jianshu.com/p/871830fc9945
- 有兴趣的读者欢迎关注我的微博:与佳期
网友评论
<Content>解析速度快,iOS重点推荐使用SAX模式解析</Content>
要读取中间的文字怎么读取呢?NSXMLParser好像只能解析标签的属性啊
if ([_currentTagName isEqualToString:@"Content"] && dict) {
[dict setObject:string forKey:@"Content"];
}
此时的字符串string就是中间的文字