IOS學習之十六:網路資料的XML解析

來源:互聯網
上載者:User

網路應用中的資料解析,因為最近的應用,無論是Android的和ios平台的,一直用也是建議用的都是Json解析,

xml解析都有點被遺忘了。

然後最近自己在做著玩一個ios的小應用,涉及網路資料的抓取,一些網站可能都提供了自己api平台,這些一般都是支援

我們對於資料協議格式的設定的。但是後來我在找尋到一些Rss資源時,發現返回的資料都是xml格式的,

因此,那就只好用xml解析了。

XML解析其實這個概念出現了算夠久了,以前javaweb什麼到處都在用。這邊我們主要大致介紹下,然後在在ios編程如何使用。

XML解析一般分兩種模式SAX和DOM,事件和文檔。具體解析google去吧,有詳細。不過看了下面的兩個例子,一般就瞭解了。

一:XML解析之SAX解析,以及對NSXMLParser的應用。

sax解析說白了,就是一個事物模型解析,從頭開始讀取文檔然後根據讀取到的頭標籤標籤時要怎麼處理,讀完頭標籤後,理論上是讀取標籤值了,

然後讀取後遇到結束標籤等

簡單舉個例子

<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"> 頭標籤,裡面的xmlns,可以看成是屬性
<channel>
<title>呵呵呵呵</title>結束標籤,中間的“呵呵呵呵”是首尾標籤標籤值空間

.......

好了,那麼在ios開發中如何使用。

SDK本身是提供了NSXMLParser解析器。

-(BOOL)parser:(NSString*)string{    //系統內建的        NSXMLParser *par = [[[NSXMLParser alloc] initWithData:[string dataUsingEncoding:NSUTF8StringEncoding]]autorelease];        [par setDelegate:self];//設定NSXMLParser對象的解析方法代理          return [par parse];//調用代理解析NSXMLParser對象,看解析是否成功   }}#pragma mark xmlparser//step 1 :準備解析- (void)parserDidStartDocument:(NSXMLParser *)parser{//    NSLog(@"%@",NSStringFromSelector(_cmd) ); }//step 2:準備解析節點- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{//    NSLog(@"%@",NSStringFromSelector(_cmd) );}//step 3:擷取首尾節點間內容- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{    NSLog(@"%@",NSStringFromSelector(_cmd) );}//step 4 :解析完當前節點- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{   NSLog(@"%@",NSStringFromSelector(_cmd) );}//step 5;解析結束- (void)parserDidEndDocument:(NSXMLParser *)parser{//    NSLog(@"%@",NSStringFromSelector(_cmd) );}//擷取cdata塊資料- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock{//    NSLog(@"%@",NSStringFromSelector(_cmd) );}

1.初始化解析器,傳入你要解析的資料。

2.parse,啟動解析,返回一個是否解析成功Bool值。

3.基本你要處理的就在下面實現的1-5個代理方法了。

其實代理方法和詳細,就是一個事物進行流程:

step1是準備解析,然後沒意外就是執行到了——>

step2讀取到第一個前端節點了,然後如果內部有屬性值,你可以擷取出來,讀完前端節點,我們會進去範圍——》

step3對於簡單的節點,可能直接就是一個string值了,但是看例子我們會知道,很多情況下,該節點的範圍包含的於是一個節點——》

這步其實分兩種,如果是值,那麼就是執行step4,擷取值的字竄,如果是子節點呢,我們一看就知道,它又是進行了step2,

即讀取到頭標籤了,其實你是很人讀一片文章流程一樣,只不過我們腦中有個印象<xxx>是頭標籤了,我們要做什麼,獨到 頭標籤的最後一個符號">"

下面進去範圍,獨到了字竄的話就調用了foundCharacters:(NSString *)string,如果又讀到<xxx>這樣的,那就又是頭標籤了。——》

step5就是讀到開始尾標籤符號了。

最後一個方法

foundCDATA:(NSData *)CDATABlock,其實也是一個格式

<content:encoded><![CDATA[<img src="http://img1.douban.com/mpic/s10489201.jpg" style="float:right;margin-left:16px"/><a href="http://www.douban.com/people/maldini/">減卐肥</a>評論: <a href="http://movie.douban.com/subject/6799191//">搜尋</a><br/> <br/>評價: 力薦<br/><br/>]]></content:encoded>

好了,方法和流程大致瞭解了,拿一個我最近遇到的例子,好多時候,我們會遇到這樣讀取一組類似於json中數組形式的資料

<channel><title>我是標題</title><link>http://write.blog.csdn.net/postedit</link><description>...</description><language>zh-cn</language><pubDate>Fri, 03 Aug 2012 06:20:31 GMT</pubDate><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item><item>...</item></channel>

一般來說,我們要的資料其實都是這20個item,對吧,每個item下都是相同的3個標籤,title,author,time。形式上其實一種數組形式那麼要怎麼解析呢1,首先我們可能先申明一個array數組容器用來存放這個20個對象,然後每個item對象中又有3個元素,那麼我們可以考慮用一個字典資料結構來表示每個item。2.需要至少申請兩個空間來作為類似於“哨兵”儲存當前執行到的節點名,以及節點的值。3.然後每次執行到讀取到item時,初始化我們上面說道的一個字典資料結構。4.在foundCharacters:方法中一直儲存當前最新的值(當然,這裡面其實會有個小瑕疵,下面會說到)。5.在標籤結束的方法,我們把標籤名和值已索引值對存入上面初始化的字典容器中。

#pragma mark xmlparser//step 1 :準備解析- (void)parserDidStartDocument:(NSXMLParser *)parser{//    NSLog(@"%@",NSStringFromSelector(_cmd) );        parserObjects = [[NSMutableArray alloc]init];}//step 2:準備解析節點- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{//    NSLog(@"%@",NSStringFromSelector(_cmd) );        self.currentText = [[NSMutableString alloc]init];    [currentText release];    if ([elementName isEqualToString:@"item"]) {        NSMutableDictionary *newNode = [[ NSMutableDictionary alloc ] initWithCapacity : 0 ];        twitterDic = newNode;        [parserObjects addObject :newNode];        [newNode release];    }    else if(twitterDic) {        NSMutableString *string = [[ NSMutableString alloc ] initWithCapacity : 0 ];        [twitterDic setObject :string forKey :elementName];        [string release ];        currentElementName = elementName;    }    }//step 3:擷取首尾節點間內容- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{    NSLog(@"%@",NSStringFromSelector(_cmd) );    [currentText appendString:string];}//step 4 :解析完當前節點- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{    if ([elementName isEqualToString:@"item"]) {        twitterDic = nil;    }else         if ([elementName isEqualToString:currentElementName]) {                    if ([elementName isEqualToString:@"description"]                ||[elementName isEqualToString:@"content:encoded"]) {                [twitterDic setObject:Cdata forKey:currentElementName];            }else {                [twitterDic setObject:currentText forKey:currentElementName];            }    }}//step 5;解析結束- (void)parserDidEndDocument:(NSXMLParser *)parser{}//擷取cdata塊資料- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock{    Cdata =[[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];}

對於上面代碼說幾點1.我次奧,有幾處記憶體泄露~~~2.在擷取值為什麼不直接currentString = string這是實踐發現的問題- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
 注意這個代理方法的注釋
   // This returns the string of the characters encountered thus far. You may not necessarily get the longest character run. The parser reserves the right to hand these to the delegate as potentially many calls in a row to -parser:foundCharacters:
下面是google翻譯。
這將返回迄今為止遇到的字元的字串。你不一定得到的最長字元運行。解析器有權交行解析器可能多次呼籲這些委託:foundCharacters:
這個說明,在擷取一個標籤首尾間的字元資料時,這個方法可能被調用多次。
舉個我碰到的最簡單的列子
<copyright>&copy; 2012, douban.com.</copyright>
我在解析這個節點時,上面方法就調用了兩次,
第一次只返回&,緊接著第二次返回copy; 2012, douban.com.
因此你如果要擷取完整的的,應該用string的append方法來擷取完整的一條記錄。3.解析速度的最佳化,比如我們只需要item中資料,那麼在獨到非這個域內的標籤,我們的哨兵不需要追中的儲存key和value值。因此我做了一處字典的釋放和判斷,在found中想減少string的賦值,當然你也可以在全域添加個標記位來控制但是總體來說,這種最佳化基本微乎其微,而且造成我記憶體莫名其妙的泄露了~~4.以上思路可以參考,代碼因為寫的太沖忙,有寫記憶體上的問題,就不要參考了,呵呵,過幾天我子在改改,可以做一個很好rss解析的模板。二:Dom文檔解析模型,TBXML第三方包應用。dom解析模型就像一個樹結構,節點,子節點,兄弟節點等等。這個其實最後被我拋棄了,這個解析器太簡化了,太簡潔的東西導致控制的進入點太少,就比如一個一鍵最佳化的軟體的概念是一樣的,一鍵清楚緩衝,最佳化配置,檔案歸類等等。人為控制就少了,導致我解析上面那個模型時,只知道遍曆儲存~。但是這個解析期對部分要求不高的xml解析其實挺好分,真的很簡潔。

//- (void)recurrence:(TBXMLElement *)element {//    //    NSString *eleName = [TBXML elementName:element]; //    NSString *eleText = [TBXML textForElement:element];//    if ([eleName isEqualToString:@"item"]) {//        self recurrence:element//    }//    //    //    //    //    do {//        //        NSString *eleName = [TBXML elementName:element]; //        NSString *eleText = [TBXML textForElement:element]; //        //        //遞迴處理子樹//        if (element->firstChild) {//            NSLog(@"<%@>:",eleName);// Display the name of the element//            //            [self recurrence:element->firstChild];//        }else {//            NSLog(@"<%@>:%@",eleName,eleText);// Display the name of the element//            //            TBXMLElement *parent = element->parentElement;//            if ([[TBXML elementName:parent] isEqualToString:@"item"]) {//                NLRssInfo *info = [[[NLRssInfo alloc]init] autorelease];//                //                if ([eleName isEqualToString:@"title"]) {//                    info.title = eleText; //                }//                //                //                [dataArr addObject:info];//            }//            //        }//        //        //        //迭代處理兄弟樹//    } while ((element = element->nextSibling));//}

遞迴遍曆,常規的樹操作,具體內容可以網上搜搜,很多。

並且開源庫的好處就是有原始碼,也就3個類,6個檔案,有興趣可以研究研究,貌似大部分代碼是用C寫的。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.