iOS系统受XML&JSON解析的代码实现

正文主要介绍了XML和JSON数据解析的基本知识,并出示了NSXMLParser方法、GDataXML第三方库以及NSJSONSerialization方法的其实行使案例。

XML和JSON是时Web开发中时常应用的号语言。这两头根本用来Web开发成功记数结构。以微博啊例,每一样长微博还发生Logo,
作者, 时间, 正文, 转发数, 回复, 点赞数
等类。这些数据在网络中都是以自然的组织存储的。

乍浪微博截图

在iOS开发中,往往用拿网络被的数目下充斥到地方,然后按自然的逻辑给呈现。这个历程尽管是常说之解析

同一、语言简介

1.XML

XML是唯独扩大标记语言(Extensible Markup
Language)
的缩写,其中的标记(markup)大凡必不可缺部分。XML语言将文件内容用限定标记进行标记,从而使每个单词、短语或段落成为可识别、可分类的音讯。更多内容可参考XML
新手入门基础知识和XML
简介。举个栗子,这个栗子节选自这里。

<?xml version="1.0" encoding="ISO-8859-1"?>
<CATALOG>
    <CD>
       <TITLE>Empire Burlesque</TITLE>
       <ARTIST>Bob Dylan</ARTIST>
       <COUNTRY>USA</COUNTRY>
       <COMPANY>Columbia</COMPANY>
       <PRICE>10.90</PRICE>
       <YEAR>1985</YEAR>
     </CD>
     <CD>
       <TITLE>Hide your heart</TITLE>
       <ARTIST>Bonnie Tyler</ARTIST>
       <COUNTRY>UK</COUNTRY>
       <COMPANY>CBS Records</COMPANY>
       <PRICE>9.90</PRICE>
       <YEAR>1988</YEAR>
     </CD>
     。。。
</CATALOG>

即时是一个CD专辑列表,其中饱含多CD专辑,每张专辑于有了标题, 作者,
国家, 出版方, 价格, 年份
等消息。其中各一个音块的内外两边都叫标记。标题让<TITLE></TITLE>标记为要素属性,专辑被<CD></CD>标记为子元素,而整张列表被<CATALOG></CATALOG>标记为根元素。整个XML文件可以是TXT文本文件,可以超过系统读写。

2.JSON

JSON (JavaScript Object Notation)
是一模一样栽轻量级的数据交换格式。它基于ECMAScript的一个子集。
JSON采用完全独立于言语的文本格式,但是呢动了接近于C语言家族的惯(包括C、C++、C#、Java、JavaScript、Perl、Python等)。这些特征使JSON成为好之数据交换语言。
易于人看和编,同时为易机器解析及变化(网络传输速率)。起源百度百科

{
 "book1": {
     "type": "textbook",
     "pages": "256",
     "title": "Programming Pearls 2nd Edition",
     "description": "The first edition of Programming Pearls was one of the most influential books I read early in my career...",
     "rating": "4.5",
     "coverType": "paperback",
     "genre": "Computer Science",
     "author": "Jon Bentley",
     "publisher": "Addison-Wesley Professional",
     "copyright": "1999"
     },
"book2": {
     ...
     },
...
 }

若果上例所示,JSON采用 数组键值对 的样式标记数据结构。

3.区别在哪

(1)JSON的频率又强

在立首文章吃,作者对XML和JSON的剖析效率展开了测试。结果表明相对XML,JSON的分析速度增长了30%,占用空间少30%

(2)XML有还多之解析方法

XML目前规划了片栽分析方法:

DOM(Document Object
Model文档对象模型)方式
。解析时得拿XML文件整体读入,并且将XML结构化成树状,使用时还通过树状结构读取相关数据,查找特定节点,然后对节点进行读或写。该办法把一个数据交换格式XML看成一个DOM对象,需要将XML文件整个读入内存,这或多或少上JSON和XML的原理是一致的,但是XML要考虑父节点和子节点,这或多或少达成JSON的分析难度要多少过多,因为JSON构建于片种结构:key/value,键值对之集聚;值的不变聚集,可分晓为数组;

SAX(Simple API for
XML)方式
。基于事件驱动的分析方法,逐行解析数据。这无异于术不需任何读入文档就得针对分析出的始末开展处理,是一律种逐步解析的方式。程序也足以天天终止解析。这样,一个不胜的文档就足以逐步的、一点一点底变现出来,所以SAX适合给广泛的分析。这或多或少,JSON时是开不交得。

总体而言:JSON独自提供整体解析方案,而这种艺术只有于分析较少的数目经常才会自及精粹的效力;XML供了针对大数据的日益解析方案,这种方案充分抱给对大气数量的拍卖。

第二、在iOS中之辨析

1.XML剖析方法简介

iOS中苹果官方提供了NSXMLParser和libxml2片栽XML解析方法,同时为闹第三正在库TBXML、TouchXML、KissXML、TinyXML、GDataXML可以推行XML解析。

个中NSXMLParser采用SAX方式分析;libxml2啊基于C语言API的开源库,可以供DOM和SAX两种植分析方法,但下比NSXMLParser麻烦。TBXML、TouchXML、KissXML、TinyXML、GDataXML等第三正库均运用DOM方式。GDataXML由于Google基被libxml2还包装得到。大神Ray
Wenderlich
在文章XML Tutorial for iOS: How To Choose The Best XML
Parser for Your iPhone
Project遇讲述了针对一一艺术的测试。作者总结道:对于读取小型XML文档,TouchXML,
KissXML,
GDataXML
足矣;如果是读写小型XML文档,KissXML和GDataXML还不错;对于大型XML文档,libxml2
SAX, TBXML, libxml2 DOM
更好。

尽管NSXMLParser表现逊于libxml2
SAX,但大以有利于,无需调用第三方库。根据原文的推荐,加上手头幸存的材料,最后自己控制学习NSXMLParser和GDataXML两种方法。

(1)NSXMLParser解析的代码实现

职责目标:(下同)采用NSXMLParser方法解析前文XML样例,并将获得的CD信息以UITableView中列下。

预告控制器头文件,注意声明代理<NSXMLParserDelegate>

#import <UIKit/UIKit.h>
@interface CDListTableViewController : UITableViewController <NSXMLParserDelegate>
@property (strong, nonatomic) NSMutableArray *dataSource;//存放解析得到的数据
@property (strong, nonatomic) NSString *startTag;
@end

据悉要拿走的CD信息,自定义了CD类

#import <Foundation/Foundation.h>

@interface CD : NSObject
@property (strong, nonatomic) NSString *title;
@property (strong, nonatomic) NSString *artist;
@property (strong, nonatomic) NSString *country;
@property (strong, nonatomic) NSString *company;
@property (strong, nonatomic) NSString *price;
@property (strong, nonatomic) NSString *year;
@end

@implementation CD
@synthesize title,artist,country,company,price,year;
@end

主控制器的贯彻文件

#import "CDListTableViewController.h"
#import "CD.h"

@implementation CDListTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    UITableView *tableView = (UITableView *)[self.view viewWithTag:1];
    UIEdgeInsets contentInset = tableView.contentInset;
    contentInset.top = 20;
    tableView.contentInset = contentInset;
    tableView.delegate = self;
    tableView.dataSource = self;

    self.dataSource = [[NSMutableArray array]init];

    NSXMLParser *aParser = [[NSXMLParser alloc]initWithContentsOfURL:[NSURL URLWithString:@"http://www.w3school.com.cn/example/xmle/cd_catalog.xml"]];
    aParser.delegate = self;
    [aParser parse];//开始解析
    aParser = nil;//释放内存
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - XML Parse

- (void)parserDidStartDocument:(NSXMLParser *)parser {
    //开始解析整个文档时调用
    NSLog(@"-START-");
}

- (void)parserDidEndDocument:(NSXMLParser *)parser {
    //结束解析整个文档时调用
    NSLog(@"-END-");
}

- (void)parser:(NSXMLParser *)parser didStartElement:(nonnull NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName attributes:(nonnull NSDictionary<NSString *,NSString *> *)attributeDict {
    //当解析遇到开始标签时调用
    NSLog(@"Did-START");
    self.startTag = elementName;
    if ([elementName isEqual:@"CD"]) {
        CD *newCD = [[CD alloc]init];
        [self.dataSource addObject:newCD];
        NSLog(@"self.dataSource has %lx Objects",[self.dataSource count]);
    }
}

- (void)parser:(NSXMLParser *)parser didEndElement:(nonnull NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName attributes:(nonnull NSDictionary<NSString *,NSString *> *)attributeDict {
    //当解析遇到结束标签时调用
    NSLog(@"Did-END");
    self.startTag = nil;
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(nonnull NSString *)string {
    //为新建的CD类实例添加信息
    NSLog(@"FOUND %@",string);
    CD *currentCD = [self.dataSource lastObject];
    if ([self.startTag isEqualToString:@"TITLE"]) {
        currentCD.title = string;
    }else if ([self.startTag isEqualToString:@"ARTIST"]){
        currentCD.artist = string;
    }else if ([self.startTag isEqualToString:@"COUNTRY"]){
        currentCD.country = string;
    }else if ([self.startTag isEqualToString:@"COMPANY"]){
        currentCD.company = string;
    }else if ([self.startTag isEqualToString:@"PRICE"]){
        currentCD.price = string;
    }else if ([self.startTag isEqualToString:@"YEAR"]){
        currentCD.year = string;
    }
    self.startTag = nil;
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.dataSource count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CDCellID" forIndexPath:indexPath];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"CDCellID"];
    }
    CD *currentCD = [[CD alloc]init];
    currentCD = [self.dataSource objectAtIndex:indexPath.row];
    cell.textLabel.text = currentCD.title;
    cell.detailTextLabel.text = currentCD.artist;
    //这里只提取了两组数据,如要显示更多信息请自定义cell
    return cell;
}
@end

运作结果Simulator Screen Shot.png

(2)GDataXML解析的代码实现

准备工作
  • 首先从这里收获GDataXMLNode.h和GDataXMLNode.m文件,并导入到眼前工程中。
  • 副,因为GDataXML是基于libxml2封闭装得到,因此还要导入libxml2仓房文件:在Linked
    Frameworks and Libraries点击加号然后搜索libxml2,双击文件即可导入。
添加libxml2
  • 连片下去,在Build Settings中寻觅“Header Search Paths”,将该价设置也
    ${SDK_DIR}/usr/include/libxml2。否则会收取报错:libxml/tree.h not found
  • 说到底一步,要拿BuildSettings中之Objective-C Automatic Reference
    Counting设置也NO。否则会吸收关于ARC的报错。
设置Objective-C Automatic Reference Counting选项
初步工作

刚刚关闭了ARC,注意搞好代码的内存管理工作。

#import "CDListTableViewController.h"
#import "GDataXMLNode.h" //导入第三方库
#import "CD.h"

@implementation CDListTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    UITableView *tableView = (UITableView *)[self.view viewWithTag:1];
    UIEdgeInsets contentInset = tableView.contentInset;
    contentInset.top = 20;
    tableView.contentInset = contentInset;
    tableView.delegate = self;
    tableView.dataSource = self;

    self.dataSource = [[NSMutableArray array]init];

    //导入整个XML文件
    GDataXMLDocument *aDocument = [[GDataXMLDocument alloc]initWithXMLString:[NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.w3school.com.cn/example/xmle/cd_catalog.xml"] encoding:NSUTF8StringEncoding error:nil] options:0 error:nil];
    //标记根元素和子元素
    GDataXMLElement *rootElement = [aDocument rootElement];
    NSArray *subElement = [rootElement elementsForName:@"CD"];
    //读取子元素
    for (GDataXMLElement *anElement in subElement) {
        CD *newCD = [[CD alloc]init];
        newCD.title = [[[anElement elementsForName:@"TITLE"] firstObject] stringValue];
        newCD.artist = [[[anElement elementsForName:@"ARTIST"] firstObject] stringValue];
        newCD.country = [[[anElement elementsForName:@"COUNTRY"] firstObject] stringValue];
        newCD.company = [[[anElement elementsForName:@"COMPANY"] firstObject] stringValue];
        newCD.price = [[[anElement elementsForName:@"PRICE"] firstObject] stringValue];
        newCD.year = [[[anElement elementsForName:@"YEAR"] firstObject] stringValue];
        [self.dataSource addObject:newCD];
        [newCD release];
    }
}


/////////////////////////////////////////////
//其余代码与上例类似,只要注意做好内存管理工作即可///
/////////////////////////////////////////////

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.   
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.dataSource count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CDCellID" forIndexPath:indexPath];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"CDCellID"];
    }
    CD *currentCD = [[CD alloc]init];
    currentCD = [self.dataSource objectAtIndex:indexPath.row];
    cell.textLabel.text = currentCD.title;
    cell.detailTextLabel.text = currentCD.artist;
    [currentCD release];
    return [cell autorelease];
}
@end

再多关于GDataXML解析的代码实现,可以参照XML Tutorial for iOS: How To
Read and Write XML Documents with
GDataXML。

2.JSON解析方法简介

苹果原生提供NSJSONSerialization解析方法,也出第三正值选择:JSONKit,SBJSON,TouchJSON。根据iOS中四种JSON解析效率比较一律温情,原生的NSJSONSerialization方法是极品选项,JSONKit是不好漂亮选择。

NSJSONSerialization解析的代码实现

职责目标:利用娱乐花边API实现NSJSONSerialization解析。

目标分析:
JSON文档有三三两两栽结构:
对象:盖“{“开始,以”}”结束,是“名称/值”对儿的集聚。名称以及值中间用“:”隔开。多单“名称/值”对中因此“,”隔开。类似Objective-C的NSDictionary。
数组:以“[“开始,以“]”结束,中间是数量。数据为“,”分割。类似Objective-C的NSArray。不同的JSON结构有两样之倒车方式。

JSON格式与Objective-C转化对照表

JSON Objective-C
大括号{} NSDictionary
中括号[] NSArray
双引号 "" NSString
数字{} NSNumber

该API返回的JSON格式数据如下所示。

{
    "0": {
        "time": "2015-07-21 19:51",
        "title": "太忙找不到好男人?你要学学Angelababy",
        "description": "太忙找不到好男人?你要学学Angelababy...",
        "picUrl": "http://img1.gtimg.com/ent/pics/hv1/33/0/1885/122572158_small.png",
        "url": "http://ent.qq.com/a/20150721/049132.htm"
    },
    "1": {
        "time": "2015-07-21 19:13",
        "title": "刘昊然晒中戏录取通知书 意外暴露接地气本名",
        "description": "刘昊然晒中戏录取通知书 意外暴露接地气本名...",
        "picUrl": "http://img1.gtimg.com/ent/pics/hv1/187/252/1884/122571547_small.jpg",
        "url": "http://ent.qq.com/a/20150721/048494.htm"
    },
    。。。
    "code": 200,
    "msg": "ok"
}

观测本例,返回对象是一个靶的点滴层嵌套,转换为Objective-C应该是片层字典。

代码实现
本着返回数据,新建了item类。简单起见,就偏偏提返回数据的题和URL两独属性。

#import <Foundation/Foundation.h>

@interface item : NSObject
@property (strong, nonatomic) NSString *title;
@property (strong, nonatomic) NSString *url;
@end

@implementation item
@synthesize title,url;
@end

为主控制器代码中导入item.h文件。这代码关闭了ARC,因此发生手动管理内存代码。

#import <UIKit/UIKit.h>
#import "item.h"

@interface ListTableViewController : UITableViewController 
@property (strong, nonatomic) NSMutableArray *dataSource;
@end

@implementation ListTableViewController

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.   
}

- (void)viewDidLoad {
    [super viewDidLoad];
    UITableView *tableView = (UITableView *)[self.view viewWithTag:1];
    UIEdgeInsets contentInset = tableView.contentInset;
    contentInset.top = 20;
    tableView.contentInset = contentInset;
    tableView.delegate = self;
    tableView.dataSource = self;
    self.dataSource = [[NSMutableArray array]init];

    //API提供了URL和 request: withHttpArg: 方法
    NSString *httpUrl = @"http://apis.baidu.com/txapi/huabian/newtop";
    NSString *httpArg = @"num=10&page=1";
    [self request: httpUrl withHttpArg: httpArg];    
}

- (void)request: (NSString*)httpUrl withHttpArg: (NSString*)HttpArg  {
    NSString *urlStr = [[NSString alloc]initWithFormat: @"%@?%@", httpUrl, HttpArg];
    NSURL *url = [NSURL URLWithString: urlStr];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL: url cachePolicy: NSURLRequestUseProtocolCachePolicy timeoutInterval: 10];
    [request setHTTPMethod: @"GET"];
    //下句中(MY-API-KEY)应为使用者自己的API Key
    [request addValue: @" (MY-API-KEY) " forHTTPHeaderField: @"apikey"];
    [NSURLConnection sendAsynchronousRequest: request
                                       queue: [NSOperationQueue mainQueue]
                           completionHandler: ^(NSURLResponse *response, NSData *data, NSError *error){
                               if (error) {
                                   NSLog(@"Httperror: %@%ld", error.localizedDescription, error.code);
                               } else {
                                   NSInteger responseCode = [(NSHTTPURLResponse *)response statusCode];
                                   NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                                   NSLog(@"HttpResponseCode:%ld", responseCode);
                                   NSLog(@"HttpResponseBody %@",responseString);
                                   //这句是自己添加的,执行对返回数据的处理
                                   [self reLoadTableViewWith:data];
                               }
                           }];
}

- (void)reLoadTableViewWith:(NSData *)data {
    //生成一级字典
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
    //遍历一级字典
    for (int k = 0; k<= [dict count]; k++) {
        NSString *count =[NSString stringWithFormat:@"%i",k];
        NSDictionary *subDict = dict[count];//生成二级字典
        item *newItem = [[item alloc]init];
        newItem.title = subDict[@"title"];
        newItem.url = subDict[@"url"];
        [self.dataSource addObject:newItem];
        [newItem release];
    }
    [self.tableView reloadData];
}


#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.dataSource count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"itemCellID" forIndexPath:indexPath];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"itemCellID"];
    }
    item *currentItem = [[item alloc]init];
    currentItem = [self.dataSource objectAtIndex:indexPath.row];
    cell.textLabel.text = currentItem.title;
    cell.detailTextLabel.text = currentItem.url;
    [currentItem release];
    return [cell autorelease];
}
@end

解析成