iOS XML/json文檔解析

引子

數據解析在iOS開發中是不可或缺的一環,從服務器獲取到的數據,就目前來說無非就是XML和json兩種。今天我就來總結一下iOS平臺下,常用的幾種解析數據的方法及步驟。

XML文件解析

關于XML文檔格式,這里不再贅述。XML百科介紹XML菜鳥學習。 關于XML解析,大致有兩種方式:

  1. SAX(Simple API for XML):事件驅動機制。從根元素開始,按順序一個個元素往下解析,它只在XML文檔中查找特定條件的內容,并且只提取需要的東西,占用內存少,也比較靈活,所以適合解析大文件。
  2. DOM(Document Object Model):文檔對象模型。一次性將整個XML文檔加載進內存,放在一個樹型結構中,需要的時候查找特定節點。實現簡單,讀寫平衡,但是比較占內存,適合解析小文件。
  3. 這里我使用的XML例子如下:(中間很長的一坨只是為了說明自帶的解析器并不會一下子把節點之間的所有字符串給解析完)。
<?xml version="1.0" encoding="UTF-8"?>
<Books>
    <Book id="1">
        <title>月亮與六便士</title>
        <author>毛姆</author>
        <summary>上帝的磨盤磨得很慢,卻磨得很細上帝的磨盤磨得
        很慢,卻磨得很細上帝的磨盤磨得很慢,卻磨得很細上帝的磨盤
        磨得很慢,卻磨得很細上帝的磨盤磨得很慢,卻磨得很細上帝的
        磨盤磨得很慢,卻磨得很細上帝的磨盤磨得很慢,卻磨得很細上
        帝的磨盤磨得很慢,卻磨得很細上帝的磨盤磨得很慢,卻磨得很
        細</summary>
    </Book>
    <Book id="2">
        <title>島上書店</title>
        <author>加布瑞埃拉·澤文</author>
        <summary>小島上書店的老板和他的書店</summary>
    </Book>
    <Book id="3">
        <title>白夜行</title>
        <author>東野圭吾</author>
        <summary>畸形但卻最深沉的愛情</summary>
    </Book>  
</Books>

NSXMLParser解析

NSXMLParser是iOS系統自帶的解析類,屬于SAX解析方式,在解析到每個元素的時候會通知代理,所以使用NSXMLParser必須遵守它的代理協議。

1. 使用步驟:

//1. 創建XML解析器
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:self.xmlData];
//2. 設置解析器的代理
parser.delegate = self;
//3. 開始解析
[parser parse];

2. 代理方法

// 1. 打開文檔,準備解析,一般在這里邊用來將保存數據的數組暫時清空
- (void)parserDidStartDocument:(NSXMLParser *)parser;

//2. 發現節點。一般在這里主要進行數據模型的初始化和讀取節點的屬性值
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
                                        namespaceURI:(NSString *)namespaceURI
                                       qualifiedName:(NSString *)qName
                                          attributes:(NSDictionary<NSString *,NSString *> *)attributeDict
{
    //節點的屬性值是以字典的形式傳遞進來的,所以取的時候也按字典的讀取方法取出來就行
    self.book.bookID = [attributeDict[@"id"] integerValue];
    
    //在準備開始解析下一個節點的數據時,先把數據清空
    [self.elementString setString:@""];
}                                       
                                          
//3. 解析節點之間的字符。 當解析器找到開始標記和結束標記之間的字符時,調用這個方法解析當前節點內的所有字符
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    //將一個節點中讀取到的數據進行拼接
    [self.elementString appendString:string];
}

//4. 節點解析結束,在這個方法里通常進行數據的保存
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
                                      namespaceURI:(NSString *)namespaceURI
                                     qualifiedName:(NSString *)qName
{
    //如果遇到了結束節點符號,則進行數據的保存
    if ([elementName isEqualToString:@"Book"]) 
    {
        [self.books addObject:self.book];
    }
    
    //如果既不是數據的最后一個節點,也不是根節點,即為數據中間的節點,則將數據對應保存到模型中
    //運用KVC保存,效率高,不過屬性的名字一定要和節點的名稱相對應
    else if(![elementName isEqualToString:@"Books"])
    {
        [self.book setValue:self.elementString forKey:elementName];
    }
    //如果節點較少,也可以通過下邊這種手動賦值的方式
    else if ([elementName isEqualToString:@"title"])
    {
       self.book.title = self.elementString;
    }
}   
                                 
//5. 文檔解析結束,可以把數據傳遞給主線程,進行相關的UI更新
- (void)parserDidEndDocument:(NSXMLParser *)parser

系統多次解析的情況:



這也是為什么要在第3步進行字符串的拼接和第2步的字符串的清空。

GDataXML解析

導入

GDataXML并沒有和cocoaPod進行關聯,所以無法使用Pod進行管理,只能從網上直接下載源文件,然后手動導入。好在GDataXML很簡單,只有一個頭文件和一個實現文件,使用的時候導入其頭文件即可。
關于導入方法,首先需要添加libxml2.tbd動態庫,然后添加兩個編譯參數,這在GDataXML.h中描述的很明白,如下:

// libxml includes require that the target Header Search Paths contain
//
//   /usr/include/libxml2
//
// and Other Linker Flags contain
//
//   -lxml2

如果遇到:

錯誤

那就這樣解決:

解決

還有這樣:

解析數據

//初始化GDataXMLDocument,將整個文檔讀入
GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:self.xmlData options:0 error:nil];
//獲取根節點
GDataXMLElement *rootElement = [doc rootElement];
//獲取根節點下的數據節點,返回值是數組類型
NSArray *Books = [rootElement elementsForName:@"Book"];
for (GDataXMLElement *book in Books)
{
    HXTXMLDataModel *bookModel = [[HXTXMLDataModel alloc]init];
    //獲取Book節點的屬性值
    bookModel.bookID = [[[book attributeForName:@"id"] stringValue]integerValue];
    //獲取Book下的數據,并且賦值給數據模型
    bookModel.title = [[[book elementsForName:@"title"]firstObject]stringValue];
    bookModel.author = [[[book elementsForName:@"author"]firstObject]stringValue];
    bookModel.summary = [[[book elementsForName:@"summary"]firstObject]stringValue];
    //將數據模型添加至全局的模型數組中
    [self.books addObject:bookModel];
}
    
HXTXMLDataModel *bookModel = self.books[0];
//取出數據,更新UI
self.Label1.text = [NSString stringWithFormat:@"%ld",bookModel.bookID];

GDataXML將文件整個讀入內存,所以取值的時候一般都會返回包含所有數據的數組類型,如果要進行數據的查找,將會非常方便!不過不能像系統自帶的那樣通過KVC賦值給數據模型,這一點也可以看出來GDataXML比較適合小的文檔解析使用。

Json文檔解析

同樣,關于json文檔格式不會涉及太多,可以json百度百科。
本例用到的json文檔:

{
    "Result": [
        {
            "id": "1",
            "title": "月亮與六便士",
            "author": "毛姆",
            "summary": "上帝的磨盤磨得很慢,卻磨得很細上帝的磨盤磨得
        很慢,卻磨得很細上帝的磨盤磨得很慢,卻磨得很細上帝的磨盤
        磨得很慢,卻磨得很細上帝的磨盤磨得很慢,卻磨得很細上帝的
        磨盤磨得很慢,卻磨得很細上帝的磨盤磨得很慢,卻磨得很細上
        帝的磨盤磨得很慢,卻磨得很細上帝的磨盤磨得很慢,卻磨得很
        細"
        },
        {
               
            "id": "2",
            "title": "島上書店",
            "author": "加布瑞埃拉·澤文",
            "summary": "小島上書店的老板和他的書店"
        },
        {
            "id": "3",
            "title": "白夜行",
            "author": "東野圭吾",
            "summary": "畸形但卻最深沉的愛情"
        },
    ]
}

本地json文件解析

其實iOS更新到現在,json解析的第三方框架也有很多,不過iOS系統自帶的解析依然效率是最高的,所以這里著重講一下系統自帶的解析。對于json解析,最重要的是看清楚文檔結構,不用一著急上來就要解析,json文檔和OC之間的對應關系大致為:

json OC
大括號 {} NSDictionary
中括號 [] NSArray
雙引號 "" NSString
數字 10、1.3 NSNumber

按照這個對應關系,我們來分析一下上邊的json文檔。

  • 首先最外層是一個大括號,所以最一開始應該用NSDictionary來接收解析出來的數據;
  • 其次所有的數據都在“Result”這個鍵對應的值里邊,所以應該用keyForValue這個方法獲取“Result”下邊的數據,而這個數據最為外層是一對中括號[],所以應該用數組來進行接收;
  • 最后中括號[]里邊依然是三個大括號{}括起來的數據,所以數組里邊的元素都是NSDictionary類型,我們用NSDictionary對數組進行遍歷,然后再次通過keyForValue獲取最后我們需要的值。當然這一步,如果數據模型的屬性和key值對應,則用KVC更是方便。
//懶加載json數據,轉換成NSData以備解析
NSString *jsonPath = [[NSBundle mainBundle]pathForResource:@"書目" ofType:@"json"];
_jsonData = [NSData dataWithContentsOfFile:jsonPath];

//1. 用jsonDict字典來接收解析出來的初步數據
self.jsonDict = [NSJSONSerialization JSONObjectWithData:self.jsonData options:0 error:nil];
//    NSLog(@"%@", self.jsonDict);
//2. 用鍵值取出Result對應的數據,保存在數組中
NSArray *resultArr = [self.jsonDict valueForKey:@"Result"];
//3. 獲取包含最終數據的字典
NSDictionary *bookDict = [resultArr firstObject];
//4. 賦值給數據模型,當然用KVC更方便
HXTXMLDataModel *bookModel = [[HXTXMLDataModel alloc]init];
bookModel.bookID = [[[resultArr firstObject] valueForKey:@"id"]integerValue];
bookModel.title = [bookDict valueForKey:@"title"];
bookModel.author = [bookDict valueForKey:@"author"];
bookModel.summary = [bookDict valueForKey:@"summary"];
//5. 根據數據進行更新
self.Label1.text = [NSString stringWithFormat:@"%ld",bookModel.bookID];
self.Label2.text = bookModel.title;
self.Label3.text = bookModel.author;
self.Label4.text = bookModel.summary;

網絡json文件解析

其實網絡json文檔解析,跟前邊本地的步驟一樣,只不過是從網絡服務器進行數據獲取,寫在這里只是為了說明,從iOS9開始,網絡請求數據的方法有些變化:

//網址
NSString *path = @"http://www.weather.com.cn/data/sk/101010100.html";
//初始化url
NSURL *url = [NSURL URLWithString:path];
//獲取網絡單例
NSURLSession *session = [NSURLSession sharedSession];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//生成請求數據的任務
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                        completionHandler:^(NSData * _Nullable data, 
                                        NSURLResponse * _Nullable response, 
                                        NSError * _Nullable error) {

    //解析json數據,跟上一個本地解析的步驟一樣,不再贅述
   
}];
//調用任務
[task resume]; 

JSONKit解析

先說一個小坑:我是通過cocoaPod導入的JSONKit庫,不過引入頭文件之后,報出20個錯誤:

錯誤

解決方法是:首先選中Xcod左邊欄Pods,然后編譯設置中做如下設置:


完工開始。
JSONKit的使用也很簡單,常用的方法有:

- (id)objectFromJSONString;
- (id)objectFromJSONStringWithParseOptions:(JKParseOptionFlags)parseOptionFlags;
- (id)objectFromJSONData;
- (id)objectFromJSONDataWithParseOptions:(JKParseOptionFlags)parseOptionFlags; 

如果數據是“單層”的,即value都是字符串、數字,可以使用objectFromJSONString或者objectFromJSONData
但是如果數據有嵌套,即value里有數組、字典、對象等,最好使用帶參數的objectFromJSONStringWithParseOptions或者objectFromJSONDataWithParseOptions。
這個例子中,我使用的是和data相關的方法。

//1. 調用JSONKit的方法進行解析,用字典接收,這里邊的枚舉值我都嘗試了,都可以得到正確結果,暫不清楚區別是什么
    self.jsonDict = [self.jsonData objectFromJSONDataWithParseOptions:JKSerializeOptionNone];
//    NSLog(@"%@====", self.jsonDict);
    
    //2. 用鍵值取出Result對應的數據,保存在數組中
    NSArray *resultArr = [self.jsonDict valueForKey:@"Result"];
    //3. 獲取包含最終數據的字典
    NSDictionary *bookDict = [resultArr firstObject];
    //4. 賦值給數據模型,當然用KVC更方便
    HXTXMLDataModel *bookModel = [[HXTXMLDataModel alloc]init];
    bookModel.bookID = [[[resultArr firstObject] valueForKey:@"id"]integerValue];
    bookModel.title = [bookDict valueForKey:@"title"];
    bookModel.author = [bookDict valueForKey:@"author"];
    bookModel.summary = [bookDict valueForKey:@"summary"];
    //5. 根據數據進行更新
    self.Label1.text = [NSString stringWithFormat:@"%ld",bookModel.bookID];
    self.Label2.text = bookModel.title;
    self.Label3.text = bookModel.author;
    self.Label4.text = bookModel.summary;

大家也都看出來了,其實只是第一步解析的不同,后邊數據處理的方法和之前是一模一樣的。
另外再提一點,我在用JSONKit解析的時候,發現它對json數據的格式比較挑剔。可以看到最上邊我那個json文檔例子,最后一個數據的大括號外邊多了一個逗號,用系統進行解析的時候沒什么問題,但是換成JSONKit之后,嘗試了幾次都解析不出來,后來打印錯誤一看,提示那個逗號不對,刪除之后再解析就正確了。希望大家引以為戒!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容