XML和JSON是两种数据交换格式。
- XML是老牌、经典、灵活的数据交换格式
- JSON是比XML轻便的数据交换格式,广泛用于Web和移动平台开发
一、XML数据解析
1、解析XML文档的两种模式
(1) SAX:事件驱动模式。从根元素开始,按顺序一个个元素往下解析,遇到开始标签、结束标签和属性等就会触发相应事件。它只在XML文档中查找特定条件的内容,并且只提取需要的东西,占用内存少,比较灵活。
优点:占用内存少,解析速度快,适合解析大文件
缺点:只能读取XML文档,不能写入或修改XML文档
(2)DOM:文档对象模型。一次性将整个XML文档加载进内存,将XML文档作为一棵树状结构进行分析,需要的时候查找特定的结点。
优点:实现简单,读写平衡(可读可写),适合解析小文件
缺点:占用内存较多,解析速度较慢,不适合解析大文件
2、常用XML解析
(1)NSXML(iOS SDK自带,苹果默认解析框架,采用SAX模式)
(2)TBXML(第三方开源库,采用DOM模式)
以下例子将使用下面的Books.xml文件来读取数据
title1
author1
summary1
title2
author2
summary2
title3
author3
summary3
3、NSXML解析
NSXML框架的核心是NSXMLParser及其委托协议NSXMLParserDelegate,主要的解析工作在NSXMLParserDelegate实现类中完成。
其中NSXMLParserDelegate有5个常用的方法:
(1)在文档开始的时候触发
parserDidStartDocument:
(2)遇到一个开始标签时触发,其中namespaceURI部分是命名空间,qualifiedName是限定名,attributes是字典类型的属性集合
parser:didStartElement:namespaceURI:qualifiedName:attributes:
(3)遇到字符串时触发
parser:foundCharacters:
(4)遇到结束标签时触发
parser:didEndElement:namespaceURI:qualifiedName:
(5)在文档结束时触发
parserDidEndDocument:
4、NSXML解析的分析:结合Books.xml文件来说明每个方法的调用时机
(此处调用方法1)
(此处调用方法2)
(此处调用方法2)
(此处调用方法2)(此处调用方法3)title1(此处调用方法4)
(此处调用方法2)(此处调用方法3)author1(此处调用方法4)
(此处调用方法2)(此处调用方法3)summary1(此处调用方法4)
(此处调用方法4)
(下同)
title2
author2
summary2
(下同)
title3
author3
summary3
(此处调用方法5)
所以我们可以自定义一个类,如KSXMLParser,并让其实现NSXMLParserDelegate协议
//KSXMLParser.h
@interface KSXMLParser : NSObject
@property (strong, nonatomic) NSMutableArray *list; //把读取的每一个Book数据作为一个数组元素保存
@property (strong, nonatomic) NSString *currentElementName; //记录当前读取的Element,如"Book","title","author"等
- (void)startParser;
@end
#import "KSXMLParser.h"
@implementation KSXMLParser
- (void)startParser {
NSString *path = [[NSBundle mainBundle] pathForResource:@"Books" ofType:@"xml"];
NSURL *url = [NSURL fileURLWithPath:path];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
parser.delegate = self;
[parser parse];
}
//方法1
- (void)parserDidStartDocument:(NSXMLParser *)parser {
self.list = [[NSMutableArray alloc] init];
}
//方法2
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
self.currentElementName = elementName;
if ([self.currentElementName isEqualToString:@"Book"]) {
NSString *identifier = [attributeDict objectForKey:@"id"];
NSMutableString *book = [[NSMutableString alloc] initWithString:identifier];
[self.list addObject: book];
}
}
//方法3
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([string isEqualToString:@""]) {
return;
}
if ([self.currentElementName isEqualToString:@"title"] && [self.list count]) {
NSMutableString * book = (NSMutableString *)[self.list lastObject];
[book appendFormat:@" %@", string];
}
if ([self.currentElementName isEqualToString:@"author"] && [self.list count]) {
NSMutableString * book = (NSMutableString *)[self.list lastObject];
[book appendFormat:@" %@", string];
}
if ([self.currentElementName isEqualToString:@"summary"] && [self.list count]) {
NSMutableString *book = (NSMutableString *)[self.list lastObject];
[book appendFormat:@" %@", string];
}
}
//方法4
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI {
}
//方法5
- (void)parserDidEndDocument:(NSXMLParser *)parser {
}
@end
//ViewController.m
#import "ViewController.h"
#import "KSXMLParser.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
KSXMLParser *parser = [[KSXMLParser alloc] init];
[parser startParser];
for (NSString *str in parser.list) {
NSLog(@"%@", str);
}
}
@end
输出
NSXMLParser[1326:111640] 1 title1 author1 summary1
NSXMLParser[1326:111640] 2 title2 author2 summary2
NSXMLParser[1326:111640] 3 title3 author3 summary3
补充说明:
方法2中的下面这2条语句是用来消除XML文本中遇到的回车符和空格的。仅仅方法2需要这2条语句,是因为方法2在遇到换行符和回车符等特殊字符也会触发,所以需要做处理。
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([string isEqualToString:@""]) {
return;
}
5、TBXML解析
因为TBXML是第三方开源库,所以不像iOS自带的那么方便,使用时需要下载,然后配置环境。
我们可以选择手动配置或使用CocoaPods帮我们配置,这里我将详细演示前者的配置方法。
(1)下载完成并解压后,将TBXML-Headers和TBXML-Code文件夹添加到工程中,如下图
(2)在工程中添加TBXML所依赖的Framework和库,有2个:libz.tbd和CoreGraphics.framework,步骤:TARGETS-->Build Phases-->Link Binary With Libraries,点击“+”按钮搜索并添加
(3)此外,还需要有预编译文件,包括两个步骤,创建和配置
通过在Xcode菜单点击File-->New-->File或快捷键Command+N选择PCH File文件模板创建预编译头文件
在预编译头文件中添加选中的那2行代码,因为TBXML默认支持MRC,是ARC_ENABLED宏可以打开ARC开关,使TBXML能够支持ARC工程。
配置预编译头文件到工程中的步骤是TARGETS-->Build Settings-->Apple LLVM 8.0 - Language --> Prefix Header中输入预编译头文件的文件名
(4)完成以上工作之后执行一次编译,如果出现以下错误:
说明在创建预编译头文件时,没有把它放到与.xcodeproj同一个目录里,把它放到那里问题就可以解决了,如下图:
6、TBXML解析的分析:
如前文第1点和第2点所述,TBXML是采用DOM模式解析的,它会将XML文件转换为一棵树进行分析,当读取Books.xml文件时,会按如下生成的树结构进行解析
解析顺序:title1,author1,summary1 --> title2,author2,summary2 --> title3,author3,summary3
7、TBXML解析
//KSXMLParser.h
#import
#import "TBXML.h"
@interface KSXMLParser : NSObject
@property (strong, nonatomic) NSMutableArray *list;
- (void)startParse;
@end
//KSXMLParser.m
#import "KSXMLParser.h"
@implementation KSXMLParser
- (void)startParse {
self.list = [[NSMutableArray alloc] init];
TBXML *tbxml = [[TBXML alloc] initWithXMLFile:@"Books.xml" error: nil];
TBXMLElement *root = tbxml.rootXMLElement;
//如果root元素有效
if (root) {
TBXMLElement *bookElement = [TBXML childElementNamed:@"Book" parentElement:root];
while (bookElement != nil) {
NSMutableString *str = [[NSMutableString alloc] init];
//获取Book id
NSString *s = [TBXML valueOfAttributeNamed:@"id" forElement:bookElement error:nil];
[str appendFormat:@" %@", s];
//获取title
TBXMLElement *titleElement = [TBXML childElementNamed:@"title" parentElement:bookElement];
if (titleElement != nil) {
NSString *s = [TBXML textForElement: titleElement];
[str appendFormat:@" %@", s];
}
//获取author
TBXMLElement *authorElement = [TBXML childElementNamed:@"author" parentElement:bookElement];
if (authorElement != nil) {
NSString *s = [TBXML textForElement:titleElement];
[str appendFormat:@" %@", s];
}
//获取summary
TBXMLElement *summaryElement = [TBXML childElementNamed:@"summary" parentElement:bookElement];
if (summaryElement != nil) {
NSString *s = [TBXML textForElement:summaryElement];
[str appendFormat:@" %@", s];
}
[self.list addObject:str];
bookElement = [TBXML nextSiblingNamed:@"Book" searchFromElement:bookElement];
}
}
}
@end
//ViewController.m
#import "ViewController.h"
#import "KSXMLParser.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
KSXMLParser *parser = [[KSXMLParser alloc] init];
[parser startParse];
for (NSString *str in parser.list) {
NSLog(@"%@", str);
}
}
@end
输出
TBXML[933:67915] 1 title1 title1 summary1
TBXML[933:67915] 2 title2 title2 summary2
TBXML[933:67915] 3 title3 title3 summary3
二、JSON数据解析
1、构成JSON文档的两种结构
- 对象,里面的每个基本单位为“名称-值”,类似于Objective-C字典,是无序集合
- 数组,里面的每个基本单位为“值”,“值”可以是双引号括起来的字符串、数值、true、false、null、对象或数组,类似于Objective-C数组,是有序集合
对象结构的语法
{
" " : " ",
" " : " ",
" " : " ",
......
}
如
{
"name" : "a.htm",
"size" : 345,
"saved" : true
}
数组结构的语法
[ " ", " ", " ", ...... ]
如
["text", "html", "css"]
对象结构和数组结构可以嵌套,如我们将在下面JSON解析时用到的data.json文件
{"ResultCode" : 0 ,
"Record" : [{"ID":"1", "title":"title1", "author":"author1", "summary":"summary1"},
{"ID":"2", "title":"title2", "author":"author2", "summary":"summary2"},
{"ID":"3", "title":"title3", "author":"author3", "summary":"summary3"}] }
2、常用JSON解析
(1)NSJSONSerialization
3、NSJSONSerialization解析
因为是在iOS5后添加进SDK的框架,所以可以很方便使用,同时也有非常优秀的性能
//ViewController.m
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"json"];
NSData *jsonData = [[NSData alloc] initWithContentsOfFile:path];
NSError *error;
id jsonObj = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
if (!jsonObj || error) {
NSLog(@"JSON解码失败");
}
NSArray *listData = jsonObj[@"Record"];
for (NSString *str in listData) {
NSLog(@"%@", str);
}
}
@end
输出
NSJSONSerialization[955:73984] {
ID = 1;
author = author1;
summary = summary1;
title = title1;
}
NSJSONSerialization[955:73984] {
ID = 2;
author = author2;
summary = summary2;
title = title2;
}
NSJSONSerialization[955:73984] {
ID = 3;
author = author3;
summary = summary3;
title = title3;
}
补充说明:
NSJSONSerialization的静态方法JSONObjectWithData: options: error:
,返回的是字典类型。
(以下出自《iOS开发指南》(第4版) p462)
其中options参数指定了解析JSON的模式。该参数是枚举类型NSJSONReadingOptions定义的,有如下3个常量。
- MutableContainers。指定解析返回的是可变的数组或字典。如果以后需要修改结果,这个常量是合适的选择
- MutableLeaves。指定叶节点是可变字符串
- AllowFragments。指定顶级节点可以不是数组或字典
此外,NSJSONSerialization还提供了JSON编码的方法, dataWithJSONObject: options: error:
和writeJSONObject: toStream: options: error: