移动端开发中,与后台服务数据请求打交道,现在通常是以JSON格式数据进行处理交互。最近碰到一个项目,后台数据交互返回XML格式数据,很久没有接触此类数据了,稍微记录下。 解析XML数据时,使用的是XMLReader第三方框架。该框架中的解析数据方法非常简单。
该框架本身提供了四种方法,以传入NSData与NSString格式的XML数据进行解析,并返回一个数据字典。
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data options:(XMLReaderOptions)options error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string options:(XMLReaderOptions)options error:(NSError **)errorPointer;
出于技术开发的特性,习惯性的进入该框架实现中,探究一下实现该框架的原理。
实现关键方法
通过上图可以发现,本身是使用了iOS原生提供的NSXMLParser类相关方法进行XML数据解析。
稍微记录一下NSXMLParser的实现过程 在NSXMLParser的代理方法中,当解析器遇到XML的根标签、数据信息中的开始标签时,会执行以下方法:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict;
开始代理实现
创建了一个堆栈字典dictionaryStack,用于管理每次执行解析代理时的新数据。 首先,在堆栈中获取当前level的字典parentDict,并将代理返回的attributeDict字典拼接到一个可变字典childDict中。 以代理方法返回的elementName为key,判断parentDict字典中是否存在以elementName为key的value。 若不存在,则在parentDict中更新elementName为key的属性,并将childDict保存。 当存在时,再继续判断是否为数组类型。若为数组,则直接在数组中添加childDict字典;若不为数组时,则创建一个新可变数组并把existingValue存入,同时将parentDict中的elementName键值替换为新创建的可变数组。 最后将childDict添加至dictionaryStack
拼接key
这个代理方法中,在获取一个标签首尾间的字符数据时,这个方法可能被调用多次。要获取完整的数据,就需要使用append方法来拼接,并存入textInProgress
结束代理实现
当前解析器执行到结束标签时,会执行- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;方法。 从dictionaryStack中获取最后一个元素,将标签首尾间的字符串进行过滤,并添加字典以text为key,键值为textInProgress。最后将textInProgress重置,并移除dictionaryStack最后一个元素。 XMLReader第三方库代码已经许久不更新了,内存管理也是基于MRC管理,如果有效率更高、更稳定的其它解析框架,欢迎补充。