iOS开发--XML&Json解析

在工作中,不可避免的我们会频繁从网络获取XML或者Json格式的数据,这些数据有着特定的数据结构,必须对其进行解析,得到我们可以处理的数据。

总结几种iOS开发中常用来解析网络数据的几种方式:

Demo传送门:https://github.com/Elbertz/ZDXXML-JsonAnalyze

Json格式:

NSJSONSerialization,官方提供的Json数据格式解析类,iOS5以后支持

SBJson

XML格式:

NSXMLParse,官方自带

XMLDictionary,好用的开源XML解析方案

GDataXML,Google提供的开源XML解析库

一、XML解析

NSXMLParser

NSXMLParser是Foundation库中提供的解析XML的类,支持从XML文件路径URL和XML数据等多种方式进行解析,常用调用接口如下:

- (nullable instancetype)initWithContentsOfURL:(NSURL *)url;  

- (instancetype)initWithData:(NSData *)data;

还有许多代理方法帮助我们获取XML数据的节点以及对应的数值。

基础使用方法:

self.par = [[NSXMLParser alloc]initWithData:data];

self.par.delegate = self;

//初始化数组,存放解析后的数据

self.list = [NSMutableArray array];

[self.par parse];

遵循NSXMLParserDelegate协议,我们可以在代理方法中获取并处理XML解析后的数据,其中最重要的回调是:

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName attributes:(NSDictionary*)attributeDict;

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName;

基础流程:

首先通过第一个回调获得节点名字以及节点属性列表,即elementName和attributeDict;

其次在第二个回调中,通过上面获取到的节点名字获取节点内容,即string;

第三个回调是当一个节点解析完毕后触发。

XMLDictionary

XMLDictionary是基于NSXMLParser的封装,在NSXMLParser进行解析过程中,如果事先并不清楚XML节点的名字,会在数据处理过程中花费很多时间。XMLDictionary就是完成了这个处理,让我们可以立即拿到字典结构的XML解析数据,方便开发。

使用之前需要做的设置:

导入XMLDictionary.h XMLDictionary.m文件


+ (nullable NSDictionary*)dictionaryWithXMLParser:(NSXMLParser *)parser;

+ (nullable NSDictionary*)dictionaryWithXMLData:(NSData *)data;

+ (nullable NSDictionary*)dictionaryWithXMLString:(NSString *)string;

+ (nullable NSDictionary*)dictionaryWithXMLFile:(NSString *)path;

从参数可知,无论你网络接收到的XML数据是以data数据还是文件还是字符串,都可以满足你的需求。

源码解析:

除了基于NSXMLParserDelegate的三个主要回调,你还需要注意以下方法:

- (void)addText:(NSString *)text;

- (void)endText;

+ (NSString *)XMLStringForNode:(id)node withNodeName:(NSString *)nodeName;

上述几个方法源码获取到XML解析的数据生成NSDictionary的关键。

- (void)parser:(__unused NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName attributes:(NSDictionary *)attributeDict{

[self endText];

NSMutableDictionary*node = [NSMutableDictionary dictionary];

。。。

node[XMLDictionaryNodeNameKey] = elementName;

。。。

for (NSString *key in attributeDict)

{

node[[XMLDictionaryAttributePrefix stringByAppendingString:key]] = attributeDict[key];

}

。。。

if (!_root){        

_root = node;        

_stack = [NSMutableArray arrayWithObject:node];        

if (_wrapRootNode)        {            

_root = [NSMutableDictionary dictionaryWithObject:_root forKey:elementName];           

 [_stack insertObject:_root atIndex:0];        

}}else{        

NSMutableDictionary*top = _stack.lastObject;

id existing = top[elementName];

if ([existing isKindOfClass:[NSArray class]])

{

[(NSMutableArray *)existing addObject:node];

}

else if (existing)

{

top[elementName] = [@[existing, node] mutableCopy];

}

else if (_alwaysUseArrays)

{

top[elementName] = [NSMutableArray arrayWithObject:node];

}

else

{

top[elementName] = node;

}

[_stack addObject:node];

}}

_root是解析完成后最终返回的字典对象;

_stack是包含_root和临时数据的一个可变数组(NSMutableArray),是_root能得到正确的XML数据的关键所在。

代码解析:根据获取到的节点名字(elementName)以及属性完成对_root的初始赋值,_stack把_root作为它本身的首项,使得后面获取到的子节点的属性以及内容,成为_root字典结构下的一部分。

- (void)parser:(__unused NSXMLParser *)parser foundCharacters:(NSString *)string

{

[self addText:string];

}

代码解析:获取节点的内容,存放在text,然后在 [self endText]中完成了对_root的写入。

GDataXML

GDataXML的解析原理是把整个xml文档一次性读出,放在一个树型结构里。在需要的时候,查找特定节点,然后对节点进行读或写。

使用之前需要做的设置:

1.导入GDataXMLNode.h和GDataXMLNode.m文件

2.引入libxml.2.2.tbd

3.选择项目的Build Settings下的

Search Paths/Header Search Paths ,加入 /usr/include/libxml2

Linking/Other Linker Flags,加入-lxml2 (Xcode8以下)

4.将GDataXMLNode.m 标记为不使用ARC: -fno-objc-arc

-(void)testGDataXMLAnalyze{

NSString *XMLPath = [[NSBundle mainBundle].bundleURL URLByAppendingPathComponent:@"config.xml" isDirectory:YES].path;

NSURL *url = [NSURL fileURLWithPath:XMLPath];

NSData *data = [NSData dataWithContentsOfURL:url];

NSLog(@"data = %@", data);

//1.首先根据XML数据生成XMLDocument对象,这个对象

GDataXMLDocument *document = [[GDataXMLDocument alloc] initWithData:data options:0 error:nil];

NSLog(@"document = %@", document);

//获取根节点

GDataXMLElement *rootElement = [document rootElement];

NSLog(@"rootElement = %@", rootElement);

//获取其他节点

NSArray *list = [rootElement elementsForName:@"List"];

NSLog(@"list = %@", list);

for (GDataXMLElement *obj in list) {

NSArray *items = [obj elementsForName:@"Item"];

NSLog(@"items = %@", items);

for (GDataXMLElement *obj in items) {

NSString *name = [[obj attributeForName:@"name"] stringValue];

NSString *value = [[obj attributeForName:@"value"] stringValue];

NSLog(@"name=%@--value=%@",name,value);

}}}

代码解析:

首先,根据XML的data生成GDataXMLDocument对象,这个对象是由libxml库解析出的标准XML数据结构构成(对于熟悉XML数据结构的童鞋很好理解)。

后面就很好理解了,根据节点结构逐一解析出所需要的节点内容。

PS:其他的还有比如像TBXML, TouchXML, KissXML, TinyXML等等,具体的使用方法可以去Github上找,后面有时间的话会分析上述XML解析方案并给出Demo。

二、Json解析

NSJSONSerialization

NSJSONSerialization是系统提供给我们的解析Json的类,用法简单:

NSString *jsonPath = [[NSBundle mainBundle].bundleURL URLByAppendingPathComponent:@"ZDXDirections.geojson" isDirectory:YES].path;

NSLog(@"jsonPath=%@",jsonPath);

。。。

NSData *data = [NSData dataWithContentsOfFile:jsonPath];

NSLog(@"data=%@",data);

[[SysJsonAnalyzeEngine shareInstance] startJsonAnalyzeWithData:data];

代码解析:我创建了一个ZDXDirections.geojson文件,存储Json 数据。在实际开发中大多是从HTTP请求中获取Json数据并进行解析。在iOS9之后,使用NSURLSession进行网络请求也很简单。

SBJson

这是一个比较久远使用比较广的一个解析Json数据的三方库。只是针对于数据解析使用方法非常简单。

NSString *jsonPath = [[NSBundle mainBundle].bundleURL URLByAppendingPathComponent:@"ZDXDirections.geojson" isDirectory:YES].path;

NSLog(@"jsonPath=%@",jsonPath);

NSData *data = [NSData dataWithContentsOfFile:jsonPath options:NSDataReadingMappedAlways error:nil];

NSLog(@"data=%@",data);

SBJsonParser *parser = [[SBJsonParser alloc]init];

NSDictionary *dic = [parser objectWithData:data];

NSLog(@"dic = %@",dic);

其他还有JSONKit、TouchJson等三方的解析库,都是使用方便应用广泛的解析Json数据的库,感兴趣的童鞋都可以根据自己的实际需求选择适合自己的解析方案。

推荐阅读更多精彩内容