在开发的时候一般都是使用JSON数据,解析方便,体积小,但是XML的基本使用还是必须要掌握的,希望能够通过阅读这篇文章帮助读者初步了解iOS开发中XML文档的解析方法,在了解XML的解析之前,有必要先说说什么是XML?
XML
跟JSON一样,XML也是经常使用的一种用于交互的数据格式,简单的来说,一个XML文档需要由以下几部分组成:
- 文档声明
- 元素(Element)
- 属性(Attribute)
文档声明:
<?xml version="1.0" encoding="UTF-8" ?>
这是一个最简单的xml文档声明,包括版本信息以及字符编码。
元素
一个XML元素包括一个开始标签和一个结束标签
<title>标题</title>
当然,一个元素也可以没有内容
<title></title>
此时我们就可以把他简写成
<title/>
元素也允许进行嵌套
<blog>
<title>我是标题</title>
<content>我是内容</content>
</blog>
其中,title和content元素为blog的子元素,虽说如此,但是元素也绝对不允许交叉嵌套
并且,一个规范的XML文档只能拥有一个根元素,其他元素都是这个根元素的子元素
属性
对于每一个元素,它可以拥有多个属性,或者没有属性
<blog title="标题" content="内容"/>
属性包括属性名和属性值,两者通过=连接,属性值需要使用“ ”或者‘ ’引用
通常我们也可以使用子元素来表示属性,
介绍了半天XML的格式,现在正式进入正题,如何解析XML:
XML解析:
在iOS的开发过程中,XML的解析方式主要有两种,今天主要介绍第二种:
- DOM:解析时会一次性将文件加载到内存中,文件体积越大越占内存,缺点越明显。
- SAX:从根元素开始一级级进行解析,使用灵活。
NSXMLParser:
苹果源生SDK,采用事件驱动(event-driven)模式,即SAX的方式来解析XML文档,当NSXMLParser在处理过程中,遇到特定的事件时会通知自身的代理,调用代理方法进行解析。
通常情况,解析一个XML需要以下步骤:
假设有一XML文档格式如下:
<books>
<book bookId="1">
<name>计算机网络</name>
<page>310</page>
<author>谢希仁</author>
</book>
<book bookId="2">
<name>数据结构</name>
<page>200</page>
<author>严蔚敏</author>
</book>
</books>
// 定义数据保存所有数据
@property (nonatomic, strong) NSMutableArray *elements;
// 定义字典保存正在处理的元素
@property (nonatomic, strong) NSMutableDictionary *currentElement;
// 定义字符串保存正在处理的内容
@property (nonatomic, strong) NSMutableString *currentContent;
1.创建一个NSXMLParser对象,并设置代理:
// data通常为服务器返回的NSData类型的数据
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
// 设置代理为本地
parser.delegate = self;
// 开始解析
BOOl isSucess = [parser parse];
if (isSuccess) {
NSLog(@"获取xml文件成功");
}else {
NSLog(@"获取xml文件失败");
}
当然必不可少的,代理需要遵守NSXMLParserDelegate协议
2.当调用解析器的Parse方法之后,程序就会开始执行代理方法解析XML文档,此处介绍需要经常使用的代理方法:
/**
* 开始解析文档的回调方法
*/
- (void)parserDidStartDocument:(NSXMLParser *)parser {
// 可以在此处初始化数组和字典以保存数据
...
}
/**
* 开始解析某一元素
* elementName:元素名
* attributeDict:元素属性字典
*/
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
// 由于我们需要保存的是book对象,所以当解析到开始标记为book时,创建一个可变字典,设置它的bookId;
if ([elementName isEqualToString:@"book"]) {
self.currentElement = [NSMutableDictionary dictionary];
self.currentElement[@"bookId"] = attributeDict[@"bookId"];
}
}
/**
* 解析元素内容,当解析器找到了开始标记和结束标记之间的内容是会调用这个方法,视内容长度,该方法可能会被重复调用多次
*/
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
// 当解析器找到内容时,拼接内容到当前处理的字符串
[self.currentString appendString:string];
}
/**
* 结束解析某一元素
* elementName:元素名
* attributeDict:元素属性字典
*/
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:@"book"]) {
// 如果找到了结束标记为book的元素,那么证明book元素以及它的所有子元素全部处理完毕,将处理完毕的book元素存入数组当中
[self.elements addObject:self.currentElement];
return;
}
// 如果标记不是book,说明此时处理完毕的元素是book元素的子元素,那么为当前处理的book元素赋值
self.currentElement[elementName] = self.currentString;
self.currentString = nil;
}
/**
* 结束解析文件
*/
- (void)parserDidEndDocument:(NSXMLParser *)parser {
...
}
/**
* 解析出现错误时的回调方法
*/
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
NSLog(@"%@",parseError);
}
NSXMLParser的好处在于其事件驱动的处理模式,条例清晰,可以在每一步骤进行对应的处理,刚看到可能会觉得很复杂,多加练习自然可以掌握。