前一篇文章已經介紹了如何通過URL從網路上擷取xml資料。下面介紹如何將擷取到的資料進行解析。
下面先看看xml的資料格式吧!
<?xml version="1.0" encoding="UTF-8"?><Books><Book id="1"><title>Circumference</title><author>Nicholas Nicastro</author><summary>Eratosthenes and the Ancient</summary></Book><Book id="2"><title>Copernicus Secret</title><author>Jack Repcheck</author><summary>How the scientific revolution began</summary></Book><Book id="3"><title>Angels and Demons</title><author>Dan Brown</author><summary>Robert Langdon is summoned to a Swiss</summary></Book></Books>
顯然在這個xml中包括三本書的一些基本資料:id title author summary 解析的過程就是將這些資料提取出來。
先簡單介紹一下xml資料解析吧。xml資料的解析一般有兩種方式:SAX(Simple API for XML)和DOM (Document Object Model),事件和文檔。
dom實現的原理是把整個xml文檔一次性讀出,放在一個樹型結構裡。在需要的時候,尋找特定節點,然後對節點進行讀或寫。他的主要優勢是實現簡單,讀寫平衡;缺點是比較占記憶體,因為他要把整個xml文檔都讀入記憶體,檔案越大,這種缺點就越明顯。
sax的實現方法和dom不同。他只在xml文檔中尋找特定條件的內容,並且只提取需要的內容。這樣做佔用記憶體小,靈活。
NSXMLParser 實現的是sax方法解析xml檔案。
下面進入主題介紹如何對xml進行解析:
一、將上面的xml資料儲存為Books.xml作為本地xml資料,並匯入項目中。
二、由於book中含有幾個屬性,所以這裡第一個book類。
Book.h檔案:
#import <Foundation/Foundation.h>@interface Book : NSObject@property (nonatomic, readwrite) NSInteger bookID;@property (nonatomic, retain) NSString *title;@property (nonatomic, retain) NSString *author;@property (nonatomic, retain) NSString *summary;@end
Book.m檔案
#import "Book.h"@implementation Book@synthesize bookID;@synthesize title;@synthesize author;@synthesize summary;@end
三、要實現對xml資料的解析實際上是通過實現NSXMLParserDelegate委託中的幾個方法。
#pragma mark xmlparser //step 1 :準備解析 - (void)parserDidStartDocument:(NSXMLParser *)parser { } //step 2:準備解析節點 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { } //step 3:擷取首尾節點間內容 - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { } //step 4 :解析完當前節點 - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { } //step 5:解析結束 - (void)parserDidEndDocument:(NSXMLParser *)parser { } //step 6:擷取cdata塊資料 - (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock { }
在實現xml解析過程以上方法中的step3 、4、5是必須的,而step1、5可選。
下面分別介紹:
Step 1: 在開始解析之前的一些準備工作,例如初始化一些儲存變數。在我的這個例子中,使用了一個Book執行個體變數來儲存一本書(也即是一組資訊),一個可變數組來儲存這三本書(即每一組資訊都當成這個數組中的一個元素),(此次要注意理解資料變數之間的關係),其他變數就暫不介紹啦(待會看原始碼就可以了)。
Step 2: 當解析器遇到xml的根標籤和一組資訊的開始標籤時就開始調用這個方法,在這個xml檔案中,遇到Books(xml檔案的根標籤),Book(一組資訊的開始標籤)時就會調用這個方法。那麼也就可以知道這個方法在程式運行過程中被調用了多次。那麼就可以用 if 語句判斷遇到的是Books還是Book,然後做一些相應的操作(具體的話待會看原始碼)。
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { }
注意其中的參數:elementName表示遇到的標籤,在這個程式中會遇到的是Books和Book; 解析到一個標籤時,開始的 標籤可能會有一些屬性,例如在這裡<Book id="1">
那麼id就是屬性啦,在這個方法中他是用一個字典來儲存的即(NSDictionary *)attributeDict,那麼對這個字典操作就可以得到你要的value和key啦。
Step 3:當解析器找到開始標籤和結束標籤之間的字元時,就調用這個方法,讀取其中的內容。 注意:這裡讀取到的string在這個函數裡面我們並不知道是那個屬性的內容,意思就是假如string的內容是Circumference(Book 的title),但是我們不知道這個是title的內容;那麼在哪裡才知道,然後對它進行儲存操作呢,不要著急,就是Step4啦!
Step 4:當解析器讀到結束標籤時,就會調用這個方法。例如讀到Books,Book,title等。那麼對讀到的標籤進行判斷後就可以進行儲存操作啦!
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { }
這裡標籤參數就是elementName。
Step 5:對整個xml檔案解析結束後的一些操作。
Step 6:這個暫不解釋(我還沒有用到)。
四、下面就是上代碼的時候啦
.h檔案
#import <UIKit/UIKit.h>@class AppDelegate,Book;@interface ViewController : UIViewController <NSXMLParserDelegate> { NSMutableString *currentElementValue; //用於儲存元素標籤的值 NSMutableArray *books; //用於儲存一組書籍 Book *aBook; //書籍執行個體,代表一本書 BOOL storingFlag; //查詢標籤所對應的元素是否存在 NSArray *elementToParse; //要儲存的元素}- (IBAction)xmlButton:(id)sender;@end
說明:這裡我用一個按鍵來觸發xml解析。
.m檔案
#import "ViewController.h"#import "AppDelegate.h"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad{ [super viewDidLoad];// Do any additional setup after loading the view, typically from a nib. //初始化要解析的元素標籤 elementToParse = [[NSArray alloc] initWithObjects:@"title",@"author",@"summary", nil];}- (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { if([elementName isEqualToString:@"Books"]) { //Initialize the array. //在這裡初始化用於儲存最終解析結果的陣列變數,我們是在當遇到Books根項目時才開始初始化 books = [[NSMutableArray alloc] init]; } else if([elementName isEqualToString:@"Book"]) { //Initialize the book. //當碰到Book元素時,初始化用於儲存Book資訊的執行個體對象aBook aBook = [[Book alloc] init]; //Extract the attribute here. //從attributeDict字典中讀取Book元素的屬性 aBook.bookID = [[attributeDict objectForKey:@"id"] integerValue]; NSLog(@"ID:%i", aBook.bookID); } storingFlag = [elementToParse containsObject:elementName]; //判斷是否存在要儲存的對象}- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { // 當用於儲存當前元素的值是空時,則先用值進行初始化賦值 // 否則就直接追加資訊 if (storingFlag) { if (!currentElementValue) { currentElementValue = [[NSMutableString alloc] initWithString:string]; } else { [currentElementValue appendString:string]; } } }// 這裡才是真正完成整個解析並儲存資料的最終結果的地方- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { if ([elementName isEqualToString:@"Book"]) { [books addObject:aBook]; aBook = nil; } if (storingFlag) { //去掉字串的空格 NSString *trimmedString = [currentElementValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; //將字串置空 [currentElementValue setString:@""]; if ([elementName isEqualToString:@"title"]) { aBook.title = trimmedString; NSLog(@"title :%@",aBook.title); } if ([elementName isEqualToString:@"author"]) { aBook.author = trimmedString; NSLog(@"author :%@",aBook.author); } if ([elementName isEqualToString:@"summary"]) { aBook.summary = trimmedString; NSLog(@"summary :%@",aBook.summary); } } }- (IBAction)xmlButton:(id)sender { //開啟xml檔案,讀取資料到NSData NSString *path = [[NSBundle mainBundle] pathForResource:@"Books" ofType:@"xml"]; NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path]; NSData *data = ; ; //測試從xml接受到的資料 NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@",dataString); NSXMLParser *m_parser = [[NSXMLParser alloc] initWithData:data]; //設定該類本身為代理類,即該類在聲明時要實現NSXMLParserDelegate委託協議 [m_parser setDelegate:self]; //設定代理為本地 BOOL flag = [m_parser parse]; //開始解析 if(flag) { NSLog(@"解析指定路徑的xml檔案成功"); } else { NSLog(@"解析指定路徑的xml檔案失敗"); } }@end
說明:
1、其中我用了一些NSLog來輸出相關的資訊驗證,實際使用過程中,去掉就ok啦!
2、結合我上面的解釋和代碼,應該可以理解整個xml解析的過程的。
3、這裡我用的是本地的xml檔案,如果是想進行網路xml資料的解析的話,那也是很簡單的,就是通過url擷取xml資料替換掉這裡的本地xml,通過url擷取xml可以參考上一篇博文。