一. XML數(shù)據(jù)交換格式
XML數(shù)據(jù)交換格式是一種自描述的數(shù)據(jù)交互格式捂襟,雖然XML數(shù)據(jù)格式不如JSON "輕便",但也是非常重要的數(shù)據(jù)交互格式趴酣。
-
XML文檔的基本結(jié)構(gòu)
XML文檔結(jié)構(gòu)要遵守一定的格式規(guī)范梨树。XML雖然形式上與HTML很相似,但是它有著嚴(yán)格的語法規(guī)則,只有嚴(yán)格按照規(guī)范編寫的XML文檔才是有效的文檔,稱為“格式良好"(well-formed)的XML文檔
先來看幾張圖
XML文檔的基本架構(gòu),可以分為下列幾個(gè)部分:
1 . 聲明:如圖3-3所示的<?xml version="1. 0" encoding="UTF-8"? > 就是XML文件的聲明,它定義XML文件的版本和使用的字符集,此例為1.0版岖寞,使用中文UTF-8 字符抡四。
2 . 根元素:如圖3-3所示的note是XML文件的根元素,<note>是根元素的開始標(biāo)簽,</note>是根元素的結(jié)束標(biāo)簽。根元素只有一個(gè),開始和結(jié)束標(biāo)簽必須一致指巡。
3 . 子元素:如圖3-3所示的to淑履、content、from和date是根元素note的子元素藻雪。所有元素都要有結(jié)束標(biāo)簽,開始和結(jié)束標(biāo)簽必須一致秘噪。如果開始標(biāo)簽和結(jié)束標(biāo)簽之間沒有內(nèi)容,可以 寫成<from/> ,稱為“空標(biāo)簽”。
4 . 屬性:圖3-4所示,是具有屬性的XML文檔阔涉。圖3-4的XML文檔中沒有屬性,屬性是定義在開始標(biāo)簽中的,在開始標(biāo)簽<Note id="1">中的id="1"是Note元素的一個(gè)屬性,id是屬性名缆娃,1是屬性值,屬性值必須放置在雙引號(hào)或單
引號(hào)之間。一個(gè)元素不能有多個(gè)相同名字的屬性瑰排。
5 . 命名空間:**用于 **一個(gè)XML文檔中提供名字唯一的元素和屬性 **贯要。例如: 在一個(gè)學(xué)籍信息的XML 文檔中需要引用到教師和學(xué)生,他們都有一個(gè)子元素id,這時(shí)候直接引用id元素會(huì)造成名稱沖突椭住。但是如果將兩個(gè)id元素放到不同的命名空間中就會(huì)解決這個(gè)問題崇渗。圖3 -5 中的“xmIns:”開頭的內(nèi)容,xmIns: xsi="http://www.w3.org/2001/XMLSchema " 等都屬于命名空間京郑。
6. ** 限定名:由命名空間引出的概念, 定義了元素和屬性的合法標(biāo)識(shí)
符宅广。限定名通常在XML 文檔中用作特定元素或?qū)傩缘囊?/strong>。圖3 -5 中的標(biāo)簽<soap:Body>就是合法的限定名些举,前綴soap是由命名空間定義的跟狱。
-
XML文檔的解析與框架性能
對于XML文檔操作包括了 “讀”與“寫”,讀人 XML 文檔 并 分析 的過程稱為 “解析”XML 文檔; 事實(shí)上,在使用XML 開發(fā)過程中户魏,“解析”XML 文檔工作占很大的比重驶臊。
讀寫XML 文檔,目前流行的有兩種模式: SAX和 DOM。
SAX 是一種基于事件驅(qū)動(dòng)的解析模式叼丑。并不需要讀入整個(gè)文檔,文檔的讀入過程也就是SAX的解析過程关翎。解析XML的時(shí)候,程序從上到下讀取XML文檔,簡單地說就是對XML文檔進(jìn)行順序掃描,當(dāng)掃描到 文檔 的 開始 與 結(jié)束 鸠信,元素(element)的 開始 與 結(jié)束 時(shí)纵寝,就會(huì)觸發(fā)相應(yīng)的事件處理 函數(shù),由事件處理函數(shù)做相應(yīng)的動(dòng)作星立,處理完后繼續(xù)掃描爽茴,直到文檔結(jié)束,則解析完畢绰垂。但是這種解析XML 文件有一個(gè)弊端就是只能讀取XML文檔,不能寫人XML 文檔闹啦。它的優(yōu)點(diǎn)是解析速度快,iOS重點(diǎn)推薦使用SAX模式解析,而且對內(nèi)存的要求通常會(huì)比較低辕坝,特別是當(dāng)開發(fā)人員只需要處理文檔中所包含的部分?jǐn)?shù)據(jù)時(shí)窍奋,SAX這種擴(kuò)展功能得到了更好的體現(xiàn)。
DOM是一種基于文檔驅(qū)動(dòng)的解析模式 。DOM 需要讀入整個(gè)XML文檔琳袄,DOM是將XML 文檔作為一棵樹狀結(jié)構(gòu)進(jìn)行分析,提供獲取節(jié)點(diǎn)的內(nèi)容,以及相關(guān)屬性江场,或者新增、刪除和修改節(jié)點(diǎn)的內(nèi)容.DOM將XML文件的元素視為樹狀結(jié)構(gòu)的節(jié)點(diǎn)窖逗,一次性讀入到內(nèi)存中址否。如果文檔比較大,解析速度就會(huì)比較慢碎紊,所以對性能和內(nèi)存的要求比較高佑附。但是DOM模式有一點(diǎn)是SAX無法取代的,就是DOM可以修改XML 文檔仗考。
延伸:SAX和DOM的區(qū)別
- SAX處理的優(yōu)點(diǎn)非常類似于流媒體的優(yōu)點(diǎn)音同。分析能夠立即開始,而不是等待所有的數(shù)據(jù)被處理秃嗜。而且由于應(yīng)用程序只是在讀取數(shù)據(jù)時(shí)檢查數(shù)據(jù)对人,因此不需要將數(shù)據(jù)存儲(chǔ)在內(nèi)存中歧寺。這對于大型文檔來說是個(gè)巨大的優(yōu)點(diǎn)葫督。事實(shí)上應(yīng)用程序甚至不必解析整個(gè)文檔妆绞;他可以在某個(gè)條件得到滿足時(shí)停止解析。一般來說必搞,SAX還比它的替代者DOM快許多必指。另一方面,由于應(yīng)用程序沒有以任何方式存儲(chǔ)數(shù)據(jù)恕洲,使用SAX還比它的替代者DOM快許多塔橡。另一方面,由于應(yīng)用程序沒有以任何方式存儲(chǔ)數(shù)據(jù)研侣,使用SAX來更改數(shù)據(jù)或在數(shù)據(jù)流中往后移是不可能的。
- DOM以及廣義的基于樹的處理有幾個(gè)優(yōu)點(diǎn)首先由于樹在內(nèi)存中是持久的炮捧,因此可以修改它以便應(yīng)用程序能對數(shù)據(jù)和結(jié)構(gòu)做出更改庶诡。它還可以在任何時(shí)候在樹中上下導(dǎo)航,而不像SAX那樣一次性的處理咆课。DOM使用起來簡單的多末誓。
- 選擇DOM還是SAX,這取決于以下幾個(gè)因素:
應(yīng)用程序的目的书蚪;數(shù)據(jù)容量喇澡。 - 對速度的需要:SAX實(shí)現(xiàn)通常要比DOM實(shí)現(xiàn)更快。
iOS SDK提供了兩個(gè)的XML框架:
1. NSXML,基于Objective-C語言的SAX解析框架殊校,它是iOS SDK默認(rèn)的XML解析框架,它不支持DOM模式晴玖。
2. libxml2,基于C語言的第三方(http://xmlsoft.org/)提供的SAX解析框架,被蘋果整合在iOSSDK中,它支持SAX和DOM模式,所以使用起來相對不太方便,但它同時(shí)支持DOM和SAX解析,尤其是它的SAX解析方式很酷,可以邊讀邊解析,非常適用于從網(wǎng)上下載一個(gè)很大的XML文件,可極大提供解析效率呕屎。
此外,iOS中解析XML還有很多第三方框架可以采用,這些框架主要有:
1. TBXML,是輕量級(jí)的DOM模式解析庫,有很好的性能和低內(nèi)存占用,不過它不對XML格式進(jìn)行校驗(yàn),不支持XPath,并且只支持解析,不支持對XML進(jìn)行修改;
2. TouchXML,基于DOM模式解析庫,支持XPath,只支持解析,不支持XML的修改;
3. KissXML,基于DOM模式解析庫,它是基于TouchXML,主要的不同是可以寫如XML文檔;
4. TinyXML,基于C++語言的DOM模式解析庫,支持對XML的讀取和修改,不直接支持XPath,需要借助TinyXPath才可以支持XPath;
5. GDataXML,基于DOM模式解析庫,是由Google開發(fā),支持讀取和修改XML文檔,支持XPath方式查詢让簿。
這么多的框架我們選擇哪一個(gè)呢?解析性能是選擇的主要指標(biāo),我自己沒測試過秀睛,別人編寫了一個(gè)測試程序來測試這些框架測試程序采用GHUnit單元測試框架尔当。
測試文件是MyNotes應(yīng)用中的XML文檔,我準(zhǔn)備了10000條Note數(shù)據(jù)的XML 文檔,保存后文件大小達(dá)到了1.2M。在第4代iPod touch設(shè)備上運(yùn)行GHUnit測試程序,結(jié)果如圖3-6所示蹂安。
從圖3-6中可以看出TBXML框架花費(fèi)時(shí)間最短椭迎,TouchXML框架花費(fèi)時(shí)間最長。當(dāng)然速度并不能說明一切,還要看看內(nèi)存占用這個(gè)指標(biāo)田盈。上面的測試程序 還可以進(jìn)行內(nèi)存占用峰值和執(zhí)行后駐留內(nèi)存比較,內(nèi)存占用峰值是衡量解析過程中占用的最大內(nèi)存畜号,它會(huì)影響應(yīng)用程序當(dāng)前運(yùn)行狀況,影響是暫時(shí)的。而執(zhí)行后駐留內(nèi)存是衡量解析完成之后內(nèi)存的駐留情況缠黍,它會(huì)影響應(yīng)用程序運(yùn)行后的狀況,影響是長期的弄兜。
使用Xcode自帶的Instrument檢查工具來檢查內(nèi)存占用情況圖3-7所示是Instrument工具運(yùn)行的結(jié)果,我們只需要查看Allocations中的最大值就是內(nèi)存占用峰值了(圖中所示是9.12MB),趨于平穩(wěn)后的內(nèi)存就是駐留內(nèi)存占用了(圖中為2.96MB)瓷式。
在圖3-8中有內(nèi)存占用峰值替饿、執(zhí)行后駐留內(nèi)存和解析時(shí)間3個(gè)指標(biāo)的比較。TouchXML應(yīng)該是最差的了,TBXML雖然是DOM解析模式,但解析速度是最快的,但是內(nèi)存占用峰值比較高,駐留內(nèi)存較低贸典。而KissXML和TinyXML也是一個(gè)不錯(cuò)的選擇,還有iOS SDK中的NSXML在速度和內(nèi)存占用都比較優(yōu)秀,如果這幾個(gè)指標(biāo)都想兼顧情況下NSXML是不錯(cuò)的選擇视卢。
我們從性能的3個(gè)主要指標(biāo)分析了這7個(gè)XML解析框架。當(dāng)然這是從用戶的角度出發(fā),如果從開發(fā)人員的角度出發(fā),除了上面的兩個(gè)指標(biāo)外,還要關(guān)注開發(fā)是否方便,以及對于XML文檔的讀寫是否支持等廊驼。它們的對比如表3-1所述据过。
在iOS下開發(fā)一般使用Objctive-C,因此從開發(fā)人員的學(xué)習(xí)曲線角度看,支持Objctive-C的框架是比較s容易使用的妒挎。但是如果讀者具有C或C++基礎(chǔ)的話,這就不是問題了绳锅。XPath是一個(gè)不錯(cuò)的技術(shù),用于XML的查詢。如果把XML文檔看做數(shù)據(jù)庫,那么XPath就相當(dāng)于SQL查詢語言酝掩。因此能夠支持XPath的框架,開發(fā)起來比較方便,這個(gè)前提是你對于XPath技術(shù)比較熟悉鳞芙。前面分析的都是讀取XML文檔,如果需要寫入XML文檔就需要注意了。
- 如果是讀取很小的XML文檔期虾,性能基本上沒有什么差別原朝,不過從調(diào)用的方便性來說,建議使用TouchXML镶苞、KissXML或GDataXML喳坠。
- 如果是需要讀取和修改XML文檔,建議使用KissXML或GDataXML茂蚓。
- 如果需要讀取非常大的XML文檔壕鹉,則建議使用libxml2或TBXML剃幌。
- 如果你不想去調(diào)用第三方類庫,那么使用NSXML也可以御板。
-
實(shí)例 : MyNotes 應(yīng)用 XML
其他第三方XML解析框架,可以百度一下
下面通過一個(gè)實(shí)例介紹NSXML框架解析XML的過程,現(xiàn)在有一個(gè)記錄我的備忘錄信息的Notes. xml文件,內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<Notes>
<Note id="1">
<CDate>2012-12-21</CDate>
<Content>早上8點(diǎn)鐘到公司</Content>
<UserID>tony</UserID>
</Note>
<Note id="2">
<CDate>2012-12-22</CDate>
<Content>發(fā)布iOSBook1</Content>
<UserID>tony</UserID>
</Note>
<Note id="3">
<CDate>2012-12-23</CDate>
<Content>發(fā)布iOSBook2</Content>
<UserID>tony</UserID>
</Note>
<Note id="4">
<CDate>2012-12-24</CDate>
<Content>發(fā)布iOSBook3</Content>
<UserID>tony</UserID>
</Note>
<Note id="5">
<CDate>2012-12-25</CDate>
<Content>發(fā)布2016奧運(yùn)會(huì)應(yīng)用iPhone版本</Content>
<UserID>tony</UserID>
</Note>
<Note id="6">
<CDate>2012-12-26</CDate>
<Content>發(fā)布2016奧運(yùn)會(huì)應(yīng)用iPad版本</Content>
<UserID>tony</UserID>
</Note>
</Notes>
文檔中的根元素是Notes,其中有很多子元素Note,每個(gè)Note 元素都有一個(gè)id 屬性(表示“備忘錄”的序號(hào)),以及3 個(gè)子元素CDate (表示“備忘錄”的日期).Content(表示“備忘錄”的內(nèi)容)和UserID(表示“備忘錄”的創(chuàng)建人ID)锥忿。
應(yīng)用運(yùn)行畫面如圖3 9所示。其中的數(shù)據(jù)來源于本地資源文件中的Notes.xml 文件怠肋。我們需要使用NSXMLParser 框架解析XML 文檔,并將數(shù)據(jù)放置于畫面表視圖中敬鬓。
1. 使用NSXML
NSXML是iOSSDK 自帶的,也是蘋果默認(rèn)解析框架,采用SAX模式解析。它是SAX解析模式的代表笙各。NSXML框架中的核心是NSXMLParser 和它的委托NSXMLParserDelegate钉答。主要的解析工作是在委托協(xié)議NSXMLParserDelegate 的實(shí)現(xiàn)類中完成的,委托中定義了很多回調(diào)方法,在SAX解析器從上圖3-9 MyNotes應(yīng)用設(shè)計(jì)到下遍歷XMI文檔的過程中,遇到開始標(biāo)簽杈抢、結(jié)束標(biāo)簽数尿、文檔 原型草圖開始、文檔結(jié)束和字符串就會(huì)觸發(fā)這些方法惶楼。這些方法有很多右蹦,我們列出5 個(gè)常用的:
(1) parserDidStartDocumet,在文檔開始的時(shí)候觸發(fā);
(2) parser:didStartElement :namespaceURI: qualifiedName: attributes,遇到一個(gè)開始標(biāo)簽時(shí)觸發(fā),其中namespaceURI 部分是命名空間,qualifiedName 是:限定名,attributes 是字典類型的屬性集合;
(3) parser:foundCharacters,遇到字符串時(shí)觸發(fā);
(4) parser:didEndElement:namespaceURIqualifiedName,遇到結(jié)東標(biāo)簽時(shí)出發(fā);
(5) parserDidEndDocument,遇到文檔結(jié)束時(shí)觸發(fā)歼捐。
其中前5個(gè)方法都是按照解析文檔的順序觸發(fā)的,理解它們先后順序很重要,可以通過圖3 -10所示的UML時(shí)序圖了解它們的觸發(fā)順序何陆。
就同一個(gè)元素而言觸發(fā)順序是按照圖3-10所示進(jìn)行的,在整個(gè)解析過程中它們的觸發(fā)次數(shù)是: 1方法和5 方法為一對,都只觸發(fā)1次豹储,2 方法和4 方法為一對贷盲,都觸發(fā)多次,3方法是在2 方法和4 方法之間觸發(fā)它也會(huì)多次觸發(fā),觸發(fā)的字符包括了換行符和回車符等特殊字符串,在編程時(shí)需要注意。
編寫了一個(gè)專門解析類NotesXMLParser,
NotesXMLParser.h 文件代碼如下:
#import <Foundation/Foundation.h>
@interface NotesXMLParser : NSObject <NSXMLParserDelegate>
//解析出的數(shù)據(jù)內(nèi)部是字典類型
@property (strong,nonatomic) NSMutableArray *notes;
//當(dāng)前標(biāo)簽的名字
@property (strong,nonatomic) NSString *currentTagName;
//開始解析
-(void)start;
@end
NotesXMLParser 實(shí)現(xiàn)NSXMLParserDelegate 協(xié)議剥扣,還定義了currentTagName 屬性的目的是在圖3 -10所示的2 方法到4 方法執(zhí)行期間臨時(shí)存儲(chǔ)正在解析的元素名,在3 方法(parser:foundCharacter )觸發(fā)時(shí)巩剖,能夠知道目前解析器處于哪個(gè)元素之中。
NotesXMLParser.m 中的start 方法代碼如下
-(void)start
{
NSString* path = [[NSBundle mainBundle] pathForResource:@"Notes" ofType:@"xml"];
NSURL *url = [NSURL fileURLWithPath:path];
//開始解析XML
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
parser.delegate = self;
[parser parse];
NSLog(@"解析完成...");
}
NSXMLParser是解析類,它有 3 個(gè)構(gòu)造方法:
1.initWithContentsOfURL,可以使用URL 對象創(chuàng)建解析對象,本例中采用的是該方法,先從資源文件中加載獲得URI對象,再使用URL對象構(gòu)建解析對象;
- initWithData,可以使用NSData 創(chuàng)建解析對象;
-
initWithStream,可以使用IO 流對象創(chuàng)建解析對象钠怯。
解析對象創(chuàng)建好后需要指定委托屬性delegate為self,然后發(fā)送parse消息,開始解析文檔佳魔。
NotesXMLParser.m 中 的方法代碼如下:
/**
文檔開始的時(shí)候觸發(fā)
只在解析開始時(shí) 觸發(fā)一次 ,因此可以在這個(gè)方法中 初始化解析過程中用到的一些 成員變量
@param parser 解析對象
*/
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
_notes = [NSMutableArray new];
}
/**
文檔出錯(cuò)的時(shí)候觸發(fā)
@param parser 解析對象
@param parseError 解析錯(cuò)誤
*/
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
NSLog(@"%@",parseError);
}
/**
遇到一個(gè)開始標(biāo)簽時(shí)候觸發(fā)
@param parser 解析對象
@param elementName 表示正在解析的元素的名字
@param namespaceURI 部分是命名空間
@param qualifiedName 限定名
@param attributeDict 字典類型的屬性集合
*/
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
_currentTagName = elementName;//需要把elementName參數(shù)賦值給成員變量_currentTagName
if ([_currentTagName isEqualToString:@"Note"]) {
NSString *_id = [attributeDict objectForKey:@"id"];//從字典中取出id屬性
NSMutableDictionary *dict = [NSMutableDictionary new];//實(shí)例化一個(gè)可變字典對象,用來存放解析出來的Note元素?cái)?shù)據(jù),成功解析之后字典中應(yīng)該有4對數(shù)據(jù),即id、CDate晦炊、Content和UserID
[dict setObject:_id forKey:@"id"];//把id放人可變字典中
[_notes addObject:dict];//把可變字典放人到可變數(shù)組集合_ notes 變量中
}
}
/**
遇到字符串時(shí)候觸發(fā)鞠鲜,該方法是解析元素文本內(nèi)容主要場所
@param parser 解析對象
@param string 字符串
,由于換行符和回車符等特殊字符也會(huì)觸發(fā)該方法,因此在第①行是用剔除換行符和回車符,其中stringByTrimmingCharactersInSet:方法是剔除字符方法,[NSCharacterSet whitespaceAndNewlineCharacterSet]指定字符集為換行符和回車符。
*/
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
//替換回車符和空格
/*
由于換行符和回車符等特殊字符也會(huì)觸發(fā)該方法,因此在第①行是用剔除換行符和回車符,
其中stringByTrimmingCharactersInSet:方法是剔除字符方法,[NSCharacterSet whitespaceAndNewlineCharacterSet]指定字符集為換行符和回車符刽锤。
*/
string =[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([string isEqualToString:@""]) {
return;
}
NSMutableDictionary *dict = [_notes lastObject];
if ([_currentTagName isEqualToString:@"CDate"] && dict) {
[dict setObject:string forKey:@"CDate"];
}
if ([_currentTagName isEqualToString:@"Content"] && dict) {
[dict setObject:string forKey:@"Content"];
}
if ([_currentTagName isEqualToString:@"UserID"] && dict) {
[dict setObject:string forKey:@"UserID"];
}
}
/**
遇到結(jié)束標(biāo)簽時(shí)候出發(fā)
在該方法中主要是清理剛剛解析完成的元素產(chǎn)生的影響镊尺,以便于不影響接下來的解析
@param parser 解析對象
@param elementName 表示正在解析的元素的名字
@param namespaceURI 部分命名空間
@param qName 限定名
*/
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName;
{
self.currentTagName = nil;//在該方法中主要是清理剛剛解析完成的元素產(chǎn)生的影響朦佩,以便于不影響接下來的解析
}
/**
遇到文檔結(jié)束時(shí)候觸發(fā)
該方法就意味著解析完成,需要清理一些成員變量并思,同時(shí)要將數(shù)據(jù)返回給表示層
(表視圖控制器),我們使用了通知機(jī)制將數(shù)據(jù)通過廣播通知投送回表示層。
@param parser 解析對象
*/
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
[[NSNotificationCenter defaultCenter] postNotificationName:@"reloadViewNotification" object:self.notes userInfo:nil];
self.notes = nil;
}
表示層的主要代碼:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reloadView:)
name:@"reloadViewNotification"
object:nil];
NotesTBXMLParser *parser = [NotesTBXMLParser new];
//開始解析
[parser start];
}
#pragma mark - 處理通知
-(void)reloadView:(NSNotification*)notification
{
NSMutableArray *resList = [notification object];
self.listData = resList;
[self.tableView reloadData];
}
二. JSON數(shù)據(jù)交換格式
JSON數(shù)據(jù)交換格式是一種輕量級(jí)的數(shù)據(jù)交換格式语稠。所謂的輕量級(jí)與XML文檔結(jié)構(gòu)相比而言宋彼,描述項(xiàng)目字符少弄砍,所以描述相同的數(shù)據(jù)的所需字符個(gè)數(shù)要少,那么傳輸?shù)乃俣染蜁?huì)提高而流量也會(huì)減少输涕。
由于
-
JSON的文檔結(jié)構(gòu)
構(gòu)成JSON 文檔兩種結(jié)構(gòu): 對象和數(shù)組音婶。
對象是 “名稱-值”對 集合,它類似于Objective-C 中的字典類型莱坎。
數(shù)組是一連串元素的集合衣式。
對象是一個(gè)無序的“名稱/值”對集合,一個(gè)對象以“{”(左括號(hào))開始檐什,“}”(右括號(hào))結(jié)束碴卧。每個(gè)“名稱”后跟一個(gè)“:”(冒號(hào)),“名稱-值”對之間使用“,”(逗號(hào))分隔,JSON 對象語法表如圖3-11所示。
下面是一個(gè)JSON的對象例子:
{
"name" :"a.htm",
"size" :345,
"saved" :true
}
數(shù)組是一個(gè)值的有序集合乃正,一個(gè)數(shù)組以“[”(左中括號(hào))開始住册,“]”(右中括號(hào))結(jié)束,值之間使用“,”(逗號(hào))分隔,JSON數(shù)組語法表如圖3-12所示。
下面是一個(gè)JSON的數(shù)組例子:
["text","html","css"]
在數(shù)組中值可以是雙引號(hào)括起來的字符串瓮具、數(shù)值荧飞、true、false名党、null叹阔、對象或者數(shù)組,而且這些結(jié)構(gòu)可以嵌套,數(shù)組中值的JSON語法結(jié)構(gòu)圖3-13所示。
-
JSON的文檔的解析與框架性能
把數(shù)據(jù)寫成JSON結(jié)構(gòu)過程稱為“編碼”過程,即寫入過程兑巾。把數(shù)據(jù)從JSON文檔中讀取處理的過程稱為“解碼”過程条获,即解析和讀取過程。
由于JSON基本比較成熟,在iOS平臺(tái)上也會(huì)有很多框架可以進(jìn)行JSON的編碼/解碼蒋歌。這些框架包括:
- SBJson是比較老的JSON編碼/解碼框架帅掘,原名是json-framework,這個(gè)框架現(xiàn)在更新仍然很頻繁,支持ARC。
- TouchJSON也是比較老的JSON編碼/解碼框架堂油,支持ARC和MRC
- YAJL是比較優(yōu)秀的JSON框架,它基于SBJson,里面進(jìn)行了優(yōu)化,底層API使用C編寫,上層API是Objective-C編寫,使用者可以有多種不同的選擇修档。它不支持ARC。
- JSONKit 是更為優(yōu)秀的JSON框架府框,它的代碼很小,但是解碼速度很快,不支持ARC,源碼下載地址https://github.com/johnezang/JSONKit吱窝。
- NextiveJson 也是非常優(yōu)秀的JSON框架,它與JSONKit性能差不多,但是在開源社區(qū)中沒有JSONKit 知名度高,不支持ARC,源碼下載地址https://github.com/nextive/NextiveJson。
- NSJSONSerialization 是iOS5 之后蘋果提供的API,它是目前非常優(yōu)秀的JSON編碼/解碼框架迫靖,支持ARC,iOS5之后的SDK就已經(jīng)包含了,不需要額外的安裝和配置,這是它的另外一個(gè)優(yōu)點(diǎn)院峡。但是如果你的應(yīng)用要兼容iOS5 之前的版本這個(gè)框架不能使用。
為了解析這些框架的性能系宜,我們可以參照XML為了解析這些框架的性能照激,我們可以參照XML實(shí)現(xiàn)MyNotes 應(yīng)用,我準(zhǔn)備了10000 條Note 數(shù)據(jù)的JSON 文檔,保存后文件大小達(dá)到了700KB,而同樣信
息的XML文檔是1.2M,這也印證JSON是輕量級(jí)的數(shù)據(jù)交互格式。同樣是在第4 代iPod touch設(shè)備上運(yùn)行GHUnit測試程序盹牧,結(jié)果如圖3-14 所示俩垃。
從圖中可以看出蘋果提供的NSJSONSerialization 框架花費(fèi)時(shí)間最短,TouchJSON 和SBJson框架花費(fèi)時(shí)間基本上最長励幼。我們再來看看內(nèi)存占用指標(biāo),使用Instrument 檢查工具來檢查內(nèi)存占用情況,最后將內(nèi)存占用峰值口柳、駐留內(nèi)存占用和上面的解碼花費(fèi)時(shí)間苹粟,一起繪制成如圖3-15所示的圖表。
在該圖表中有內(nèi)存占用峰值跃闹、執(zhí)行后駐留內(nèi)存和解析時(shí)間3 個(gè)指標(biāo)的比較嵌削。TouchJSON和SBJson應(yīng)該是很差的,事實(shí)上SBJson 在iOS 5 之前的用戶很多。NSJSONSerialization 是解
碼速度最快的,內(nèi)存占用峰值是最低的,可見NSJSONSerializatio是一個(gè)非常優(yōu)秀的JSON解碼框架望艺,但是它的執(zhí)行后駐留內(nèi)存卻比Nextivelson要高, NextiveJson解碼速度也是比較快的掷贾,內(nèi)存峰值要比NSJSONSerialization略高一些。
事實(shí)上,執(zhí)行后駐留內(nèi)存多少對于應(yīng)用程序的影響是比較大的荣茫,而且是長期的影響,這些內(nèi)存不經(jīng)過特殊釋放就會(huì)一直保持在那里想帅。它們將伴隨整個(gè)應(yīng)用程序生命周期,直到應(yīng)用被終止才被釋放,對于整個(gè)設(shè)備都會(huì)有比較大的影響啡莉。
因此,綜合考慮,如果需要考慮兼容iOS5之前的版本,NextiveJson和JSONKit都是不錯(cuò)的選擇港准。它們都不支持ARC,使用起來有點(diǎn)麻煩,需要安裝和配置到工程環(huán)境中。
-
實(shí)例: MyNotes應(yīng)用JSON解碼
上一節(jié)從性能的3個(gè)主要指標(biāo)分析了這6個(gè)JSON編碼/解碼框架咧欣。這一節(jié)介紹一下NSJSONSerialization如何實(shí)現(xiàn)JSON的解碼的浅缸。實(shí)例還是采用MyNote應(yīng)用,重新設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)為JSON格式,備忘錄信息的Notes. json文件,它的內(nèi)容如下:
{"ResultCode":0,
"Record":[
{"ID":"1","CDate":"2012-12-23","Content":"發(fā)布iOSBook0","UserID":"tony"},
{"ID":"2","CDate":"2012-12-24","Content":"發(fā)布iOSBook1","UserID":"tony"},
{"ID":"3","CDate":"2012-12-25","Content":"發(fā)布iOSBook2","UserID":"tony"},
{"ID":"4","CDate":"2012-12-26","Content":"發(fā)布iOSBook3","UserID":"tony"},
{"ID":"5","CDate":"2012-12-27","Content":"發(fā)布iOSBook4","UserID":"tony"}
]
}
注意:在上面介紹的6個(gè)框架中對于JSON文檔的結(jié)構(gòu)要求比較嚴(yán)格魄咕,每個(gè)JSON數(shù)據(jù)項(xiàng)目“名稱”必須使用雙引號(hào)括起來,不能使用單引號(hào)或沒有引號(hào)衩椒,如下代碼文檔中“名稱”省略雙引號(hào),該文檔在iOS平臺(tái)解析時(shí)會(huì)出現(xiàn)異常,而在Java等其他平臺(tái)就沒有這些限制,也不會(huì)出現(xiàn)異常,而我們的JSON數(shù)據(jù)很多情況下編碼和解碼并非是一種語言的哮兰。
{ResultCode:0,
Record:[
{ID:'1',CDate:'2012-12-23',Content:'發(fā)布iOSBook0',UserID:'tony'},
{ID:'2',CDate:'2012-12-24',Content:'發(fā)布iOSBook1',UserID:'tony'}
]
}
JSON的解碼過程就是將字符串,分析之后讀入到一個(gè)集合對象中,這個(gè)集合對象的結(jié)構(gòu)可能是數(shù)組毛萌,也可能是字典。Notes. json解碼之后整個(gè)的結(jié)構(gòu)是一個(gè)字典,這個(gè)字典有兩個(gè)“名字值”,與Record名字對應(yīng)的值是一個(gè)數(shù)組,而數(shù)組中的每一個(gè)元素又是一個(gè)字典對象喝滞。取得其中的內(nèi)容也相似“剝洋蔥皮”阁将。
使用NSJSONSerialization實(shí)現(xiàn)解碼過程是非常簡單的,因?yàn)楹唵?所以沒有必要再為解碼單獨(dú)創(chuàng)建一個(gè)類,直接使用就可以了。
- (void)viewDidLoad
{
NSString* path = [[NSBundle mainBundle] pathForResource:@"Notes" ofType:@"json"];
NSData *jsonData = [[NSData alloc] initWithContentsOfFile:path];
NSError *error;
/*
使用NSJSONSerialization的類方法JSONObjectWithData: options; error;進(jìn)行解碼其中options參數(shù)指定了解析JSON模式,它是枚舉類型NSJSONReadingOptions中定義了三個(gè)常量:
(1) NSJSONReadingMutableContainers,指定解析返回的是可變的數(shù)組或字典右遭,如果以后需要修改結(jié)果,這個(gè)常量是合適的選擇;
(2) NSJSONReadingMutableLeaves,指定葉節(jié)點(diǎn)是可變字符串;
(3) NSJSONReadingAllowFragments,指定頂級(jí)節(jié)點(diǎn)可以不是數(shù)組或字典做盅。
此外,NSJSONSerialization還提供了JSON編碼的方法: dataWithJSONObject;options:error:和writeJSONObject; toStream; options:error:,關(guān)于JSON的編碼方法的使用與解碼非常類似
*/
id jsonObj = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers error:&error];
if (!jsonObj || error) {
NSLog(@"JSON解碼失敗");
}else{
self.listData = [jsonObj objectForKey:@"Record"];
//然后處理數(shù)據(jù)和刷新界面
}
}
JSON是輕量級(jí)的數(shù)據(jù)交換格式,在應(yīng)用開發(fā)時(shí)候優(yōu)先考慮使用JSON而不是XML窘哈。XML的最優(yōu)解析框架是TBXML,JSON的最優(yōu)解碼框架是NSJSONSerialization吹榴。
-
總結(jié)
XML與JSON兩種數(shù)據(jù)結(jié)構(gòu)的優(yōu)缺點(diǎn)
- XML
優(yōu)點(diǎn):
- 格式統(tǒng)一, 符合標(biāo)準(zhǔn)
- 容易與其他系統(tǒng)進(jìn)行遠(yuǎn)程交互, 數(shù)據(jù)共享比較方便
缺點(diǎn):
XML文件格式文件龐大, 格式復(fù)雜, 傳輸占用帶寬
服務(wù)器端和客戶端都需要花費(fèi)大量代碼來解析XML, 不論服務(wù)器端和客戶端代碼變的異常復(fù)雜和不容易維護(hù)
客戶端不同,瀏覽器之間解析XML的方式不一致, 需要重復(fù)編寫很多代碼
服務(wù)器端 和 客戶端 解析XML花費(fèi)資源和時(shí)間
- JSON
優(yōu)點(diǎn):
數(shù)據(jù)格式比較簡單, 易于讀寫, 格式都是壓縮的, 占用帶寬小
易于解析這種語言
支持多種語言, 包括ActionScript, C, C#, ColdFusion, Java, JavaScript, Perl, PHP, Python, Ruby等語言服務(wù)器端語言, 便于服務(wù)器端的解析
因?yàn)镴SON格式能夠直接為服務(wù)器端代碼使用, 大大簡化了服務(wù)器端和客戶端的代碼開發(fā)量, 但是完成的任務(wù)不變, 且易于維護(hù)
缺點(diǎn):
- 沒有XML格式這么推廣的深入人心和使用廣泛, 沒有XML那么通用性
- JSON格式目前在Web Service中推廣還屬于初級(jí)階段
XML和JSON的優(yōu)缺點(diǎn)對比
-
可讀性方面
JSON和XML的數(shù)據(jù)可讀性基本相同滚婉,JSON和XML的可讀性可謂不相上下图筹,一邊是建議的語法,一邊是規(guī)范的標(biāo)簽形式满哪,XML可讀性較好些婿斥。 -
可擴(kuò)展性方面
XML天生有很好的擴(kuò)展性,JSON當(dāng)然也有哨鸭,沒有什么是XML能擴(kuò)展民宿,JSON不能的。都具有很好的擴(kuò)展性像鸡。 -
編碼難度方面
XML有豐富的編碼工具活鹰,比如Dom4j、JDom等只估,JSON也有json.org提供的工具志群,但是JSON的編碼明顯比XML容易許多,即使不借助工具也能寫出JSON的代碼蛔钙,可是要寫好XML就不太容易了锌云。相對而言:JSON的編碼比較容易。 -
解碼難度方面
json解碼難度基本為零吁脱,xml需要考慮子節(jié)點(diǎn)和父節(jié)點(diǎn)桑涎。 -
傳輸速度方面
JSON的速度要遠(yuǎn)遠(yuǎn)快于XML -
流行度方面
XML已經(jīng)被業(yè)界廣泛的使用,而JSON才剛剛開始兼贡,但是在Ajax這個(gè)特定的領(lǐng)域攻冷,未來的發(fā)展一定是XML讓位于JSON。到時(shí)Ajax應(yīng)該變成Ajaj(Asynchronous Javascript and JSON)了遍希。 -
解析手段方面
JSON和XML同樣擁有豐富的解析手段等曼。 -
數(shù)據(jù)體積方面
JSON相對于XML來講,數(shù)據(jù)的體積小凿蒜,傳遞的速度更快些禁谦。 -
數(shù)據(jù)交互方面
JSON與JavaScript的交互更加方便,更容易解析處理废封,更好的數(shù)據(jù)交互枷畏。 -
數(shù)據(jù)描述方面
JSON對數(shù)據(jù)的描述性比XML較差。
-
XML與JSON數(shù)據(jù)格式比較
關(guān)于輕量級(jí)和重量級(jí)
輕量級(jí)和重量級(jí)是相對來說的虱饿,那么XML相對于JSON的重量級(jí)體現(xiàn)在哪呢?應(yīng)該體現(xiàn)在解析上拥诡,XML目前設(shè)計(jì)了兩種解析方式:DOM和 SAX。
- DOM
DOM是把一個(gè)數(shù)據(jù)交換格式XML看成一個(gè)DOM對象氮发,需要把XML文件整個(gè)讀入內(nèi)存渴肉,這一點(diǎn)上JSON和XML的原理是一樣的,但是XML要考慮父節(jié)點(diǎn)和子節(jié)點(diǎn)爽冕,這一點(diǎn)上JSON的解析難度要小很多仇祭,因?yàn)镴SON構(gòu)建于兩種結(jié)構(gòu):key/value,鍵值對的集合;值的有序集合颈畸,可理解為數(shù)組乌奇; - SAX
SAX不需要整個(gè)讀入文檔就可以對解析出的內(nèi)容進(jìn)行處理没讲,是一種逐步解析的方法。程序也可以隨時(shí)終止解析礁苗。這樣爬凑,一個(gè)大的文檔就可以逐步的、一點(diǎn)一點(diǎn)的展現(xiàn)出來试伙,所以SAX適合于大規(guī)模的解析嘁信。這一點(diǎn),JSON目前是做不到得疏叨。
所以潘靖,JSON和XML的輕/重量級(jí)的區(qū)別在于:
JSON只提供整體解析方案,而這種方法只在解析較少的數(shù)據(jù)時(shí)才能起到良好的效果蚤蔓;
XML提供了對大規(guī)模數(shù)據(jù)的逐步解析方案卦溢,這種方案很適合于對大量數(shù)據(jù)的處理。
關(guān)于數(shù)據(jù)格式編碼及解析難度
在編碼方面秀又。
雖然XML和JSON都有各自的編碼工具既绕,但是JSON的編碼要比XML簡單,即使不借助工具涮坐,也可以寫出JSON代碼凄贩,但要寫出好的XML代碼就有點(diǎn)困難;與XML一樣,JSON也是基于文本的袱讹,且它們都使用Unicode編碼疲扎,且其與數(shù)據(jù)交換格式XML一樣具有可讀性。
主觀上來看捷雕,JSON更為清晰且冗余更少些椒丧。JSON網(wǎng)站提供了對JSON語法的嚴(yán)格描述,只是描述較簡短救巷。從總體來看壶熏,XML比較適合于標(biāo)記文檔,而JSON卻更適于進(jìn)行數(shù)據(jù)交換處理浦译。在解析方面棒假。
在普通的web應(yīng)用領(lǐng)域,開發(fā)者經(jīng)常為XML的解析傷腦筋精盅,無論是服務(wù)器端生成或處理XML帽哑,還是客戶端用 JavaScript 解析XML,都常常導(dǎo)致復(fù)雜的代碼叹俏,極低的開發(fā)效率妻枕。
實(shí)際上,對于大多數(shù)Web應(yīng)用來說,他們根本不需要復(fù)雜的XML來傳輸數(shù)據(jù)屡谐,XML宣稱的擴(kuò)展性在此就很少具有優(yōu)勢,許多Ajax應(yīng)用甚至直接返回HTML片段來構(gòu)建動(dòng)態(tài)Web頁面述么。和返回XML并解析它相比,返回HTML片段大大降低了系統(tǒng)的復(fù)雜性愕掏,但同時(shí)缺少了一定的靈活性度秘。同XML或 HTML片段相比,數(shù)據(jù)交換格式JSON 提供了更好的簡單性和靈活性亭珍。在Web Serivice應(yīng)用中,至少就目前來說XML仍有不可動(dòng)搖的地位枝哄。實(shí)例比較
XML和JSON都使用結(jié)構(gòu)化方法來標(biāo)記數(shù)據(jù)肄梨,下面來做一個(gè)簡單的比較。
<?xml version="1.0" encoding="utf-8" ?>
<country>
<name>中國</name>
<province>
<name>黑龍江</name>
<citys>
<city>哈爾濱</city>
<city>大慶</city>
</citys>
</province>
<province>
<name>廣東</name>
<citys>
<city>廣州</city>
<city>深圳</city>
<city>珠海</city>
</citys>
</province>
<province>
<name>臺(tái)灣</name>
<citys>
<city>臺(tái)北</city>
<city>高雄</city>
</citys>
</province>
<province>
<name>新疆</name>
<citys>
<city>烏魯木齊</city>
</citys>
</province>
</country>
用JSON表示中國部分省市數(shù)據(jù)如下
{
name: "中國",
provinces: [
{ name: "黑龍江", citys: { city: ["哈爾濱", "大慶"]} },
{ name: "廣東", citys: { city: ["廣州", "深圳", "珠海"]} },
{ name: "臺(tái)灣", citys: { city: ["臺(tái)北", "高雄"]} },
{ name: "新疆", citys: { city: ["烏魯木齊"]} }
]
}
編碼的可讀性來說挠锥,XML有明顯的優(yōu)勢众羡,畢竟人類的語言更貼近這樣的說明結(jié)構(gòu)。JSON讀起來更像一個(gè)數(shù)據(jù)塊蓖租,讀起來就比較費(fèi)解了粱侣。不過,我們讀起來費(fèi)解的語言蓖宦,恰恰是適合機(jī)器閱讀齐婴,所以通過JSON的索引country.provinces[0].name就能夠讀取“黑龍江”這個(gè)值。
編碼的手寫難度來說稠茂,XML還是舒服一些柠偶,好讀當(dāng)然就好寫。不過寫出來的字符JSON就明顯少很多睬关。去掉空白制表以及換行的話诱担,JSON就是密密麻麻的有用數(shù)據(jù),而XML卻包含很多重復(fù)的標(biāo)記字符电爹。
參考文獻(xiàn):
JSON與XML的區(qū)別比較
IOS網(wǎng)絡(luò)編程與云端應(yīng)用最佳實(shí)踐.pdf