XML和JSON是兩種數(shù)據(jù)交換格式运翼。
- XML是老牌双谆、經(jīng)典壳咕、靈活的數(shù)據(jù)交換格式
- JSON是比XML輕便的數(shù)據(jù)交換格式席揽,廣泛用于Web和移動(dòng)平臺(tái)開(kāi)發(fā)
一、XML數(shù)據(jù)解析
1谓厘、解析XML文檔的兩種模式
(1) SAX:事件驅(qū)動(dòng)模式幌羞。從根元素開(kāi)始,按順序一個(gè)個(gè)元素往下解析竟稳,遇到開(kāi)始標(biāo)簽属桦、結(jié)束標(biāo)簽和屬性等就會(huì)觸發(fā)相應(yīng)事件。它只在XML文檔中查找特定條件的內(nèi)容他爸,并且只提取需要的東西聂宾,占用內(nèi)存少,比較靈活诊笤。
優(yōu)點(diǎn):占用內(nèi)存少系谐,解析速度快,適合解析大文件
缺點(diǎn):只能讀取XML文檔讨跟,不能寫(xiě)入或修改XML文檔
(2)DOM:文檔對(duì)象模型纪他。一次性將整個(gè)XML文檔加載進(jìn)內(nèi)存,將XML文檔作為一棵樹(shù)狀結(jié)構(gòu)進(jìn)行分析晾匠,需要的時(shí)候查找特定的結(jié)點(diǎn)茶袒。
優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單,讀寫(xiě)平衡(可讀可寫(xiě))凉馆,適合解析小文件
缺點(diǎn):占用內(nèi)存較多薪寓,解析速度較慢,不適合解析大文件
2澜共、常用XML解析
(1)NSXML(iOS SDK自帶预愤,蘋(píng)果默認(rèn)解析框架,采用SAX模式)
(2)TBXML(第三方開(kāi)源庫(kù)咳胃,采用DOM模式)
以下例子將使用下面的Books.xml文件來(lái)讀取數(shù)據(jù)
<?xml version="1.0" encoding="UTF-8"?>
<Books>
<Book id="1">
<title>title1</title>
<author>author1</author>
<summary>summary1</summary>
</Book>
<Book id="2">
<title>title2</title>
<author>author2</author>
<summary>summary2</summary>
</Book>
<Book id="3">
<title>title3</title>
<author>author3</author>
<summary>summary3</summary>
</Book>
</Books>
</br>
3植康、NSXML解析
NSXML框架的核心是NSXMLParser及其委托協(xié)議NSXMLParserDelegate,主要的解析工作在NSXMLParserDelegate實(shí)現(xiàn)類中完成展懈。
其中NSXMLParserDelegate有5個(gè)常用的方法:
(1)在文檔開(kāi)始的時(shí)候觸發(fā)
parserDidStartDocument:
(2)遇到一個(gè)開(kāi)始標(biāo)簽時(shí)觸發(fā)销睁,其中namespaceURI部分是命名空間,qualifiedName是限定名存崖,attributes是字典類型的屬性集合
parser:didStartElement:namespaceURI:qualifiedName:attributes:
(3)遇到字符串時(shí)觸發(fā)
parser:foundCharacters:
(4)遇到結(jié)束標(biāo)簽時(shí)觸發(fā)
parser:didEndElement:namespaceURI:qualifiedName:
(5)在文檔結(jié)束時(shí)觸發(fā)
parserDidEndDocument:
4冻记、NSXML解析的分析:結(jié)合Books.xml文件來(lái)說(shuō)明每個(gè)方法的調(diào)用時(shí)機(jī)
(此處調(diào)用方法1)
<?xml version="1.0" encoding="UTF-8"?>
(此處調(diào)用方法2)<Books>
(此處調(diào)用方法2)<Book id="1">
(此處調(diào)用方法2)<title>(此處調(diào)用方法3)title1(此處調(diào)用方法4)</title>
(此處調(diào)用方法2)<author>(此處調(diào)用方法3)author1(此處調(diào)用方法4)</author>
(此處調(diào)用方法2)<summary>(此處調(diào)用方法3)summary1(此處調(diào)用方法4)</summary>
(此處調(diào)用方法4) </Book>
(下同)
<Book id="2">
<title>title2</title>
<author>author2</author>
<summary>summary2</summary>
</Book>
(下同)
<Book id="3">
<title>title3</title>
<author>author3</author>
<summary>summary3</summary>
</Book>
</Books>
(此處調(diào)用方法5)
</br>
所以我們可以自定義一個(gè)類,如KSXMLParser来惧,并讓其實(shí)現(xiàn)NSXMLParserDelegate協(xié)議
//KSXMLParser.h
@interface KSXMLParser : NSObject <NSXMLParserDelegate>
@property (strong, nonatomic) NSMutableArray *list; //把讀取的每一個(gè)Book數(shù)據(jù)作為一個(gè)數(shù)組元素保存
@property (strong, nonatomic) NSString *currentElementName; //記錄當(dāng)前讀取的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<NSString *,NSString *> *)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
補(bǔ)充說(shuō)明:
方法2中的下面這2條語(yǔ)句是用來(lái)消除XML文本中遇到的回車符和空格的隅居。僅僅方法2需要這2條語(yǔ)句钠至,是因?yàn)榉椒?在遇到換行符和回車符等特殊字符也會(huì)觸發(fā),所以需要做處理胎源。
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([string isEqualToString:@""]) {
return;
}
</br>
5棉钧、TBXML解析
因?yàn)門BXML是第三方開(kāi)源庫(kù),所以不像iOS自帶的那么方便涕蚤,使用時(shí)需要下載宪卿,然后配置環(huán)境。
我們可以選擇手動(dòng)配置或使用CocoaPods幫我們配置万栅,這里我將詳細(xì)演示前者的配置方法佑钾。
(1)下載完成并解壓后,將TBXML-Headers和TBXML-Code文件夾添加到工程中烦粒,如下圖
(2)在工程中添加TBXML所依賴的Framework和庫(kù)休溶,有2個(gè):libz.tbd和CoreGraphics.framework,步驟:TARGETS-->Build Phases-->Link Binary With Libraries撒遣,點(diǎn)擊“+”按鈕搜索并添加
(3)此外邮偎,還需要有預(yù)編譯文件管跺,包括兩個(gè)步驟义黎,創(chuàng)建和配置
通過(guò)在Xcode菜單點(diǎn)擊File-->New-->File或快捷鍵Command+N選擇PCH File文件模板創(chuàng)建預(yù)編譯頭文件
在預(yù)編譯頭文件中添加選中的那2行代碼,因?yàn)門BXML默認(rèn)支持MRC豁跑,是ARC_ENABLED宏可以打開(kāi)ARC開(kāi)關(guān)廉涕,使TBXML能夠支持ARC工程。
配置預(yù)編譯頭文件到工程中的步驟是TARGETS-->Build Settings-->Apple LLVM 8.0 - Language --> Prefix Header中輸入預(yù)編譯頭文件的文件名
(4)完成以上工作之后執(zhí)行一次編譯艇拍,如果出現(xiàn)以下錯(cuò)誤:
說(shuō)明在創(chuàng)建預(yù)編譯頭文件時(shí)狐蜕,沒(méi)有把它放到與.xcodeproj同一個(gè)目錄里,把它放到那里問(wèn)題就可以解決了卸夕,如下圖:
6层释、TBXML解析的分析:
如前文第1點(diǎn)和第2點(diǎn)所述,TBXML是采用DOM模式解析的快集,它會(huì)將XML文件轉(zhuǎn)換為一棵樹(shù)進(jìn)行分析贡羔,當(dāng)讀取Books.xml文件時(shí),會(huì)按如下生成的樹(shù)結(jié)構(gòu)進(jìn)行解析
解析順序:title1个初,author1乖寒,summary1 --> title2,author2院溺,summary2 --> title3楣嘁,author3,summary3
7、TBXML解析
//KSXMLParser.h
#import <Foundation/Foundation.h>
#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
</br>
二逐虚、JSON數(shù)據(jù)解析
1聋溜、構(gòu)成JSON文檔的兩種結(jié)構(gòu)
- 對(duì)象,里面的每個(gè)基本單位為“名稱-值”痊班,類似于Objective-C字典勤婚,是無(wú)序集合
- 數(shù)組,里面的每個(gè)基本單位為“值”涤伐,“值”可以是雙引號(hào)括起來(lái)的字符串馒胆、數(shù)值、true凝果、false祝迂、null、對(duì)象或數(shù)組器净,類似于Objective-C數(shù)組型雳,是有序集合
對(duì)象結(jié)構(gòu)的語(yǔ)法
{
" " : " ",
" " : " ",
" " : " ",
......
}
如
{
"name" : "a.htm",
"size" : 345,
"saved" : true
}
數(shù)組結(jié)構(gòu)的語(yǔ)法
[ " ", " ", " ", ...... ]
如
["text", "html", "css"]
對(duì)象結(jié)構(gòu)和數(shù)組結(jié)構(gòu)可以嵌套,如我們將在下面JSON解析時(shí)用到的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"}] }
</br>
2山害、常用JSON解析
(1)NSJSONSerialization
3纠俭、NSJSONSerialization解析
因?yàn)槭窃趇OS5后添加進(jìn)SDK的框架,所以可以很方便使用浪慌,同時(shí)也有非常優(yōu)秀的性能
//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;
}
補(bǔ)充說(shuō)明:
NSJSONSerialization的靜態(tài)方法JSONObjectWithData: options: error:
冤荆,返回的是字典類型。
(以下出自《iOS開(kāi)發(fā)指南》(第4版) p462)
其中options參數(shù)指定了解析JSON的模式权纤。該參數(shù)是枚舉類型NSJSONReadingOptions定義的钓简,有如下3個(gè)常量。
- MutableContainers汹想。指定解析返回的是可變的數(shù)組或字典外邓。如果以后需要修改結(jié)果,這個(gè)常量是合適的選擇
- MutableLeaves古掏。指定葉節(jié)點(diǎn)是可變字符串
- AllowFragments损话。指定頂級(jí)節(jié)點(diǎn)可以不是數(shù)組或字典
此外,NSJSONSerialization還提供了JSON編碼的方法槽唾, dataWithJSONObject: options: error:
和writeJSONObject: toStream: options: error: