iOS 中 XML 解析 NSXMLParse和GDataXMLNode的使用

XML解析

  • 可擴(kuò)展標(biāo)記語(yǔ)言
  • XML的特點(diǎn),出身名門(mén),W3C制定,微軟和IBM曾經(jīng)共同大力推薦過(guò)的數(shù)據(jù)格式
  • XML 指可擴(kuò)展標(biāo)記語(yǔ)言(eXtensible Markup Language)
    • 被設(shè)計(jì)用來(lái)傳輸和存儲(chǔ)數(shù)據(jù)
    • HTML 是設(shè)計(jì)用來(lái)表示頁(yè)面的

SAX解析

SAX是iOS默認(rèn)的解析XML的方式,simple API for XML . 是一種占用內(nèi)存非常低,但是只能讀取不能寫(xiě)入的解析方式.因?yàn)樗且恍幸恍械慕馕龅?

  • 準(zhǔn)備一個(gè)模型以及兩個(gè)屬性接收解析出來(lái)的數(shù)據(jù).
#import "ViewController.h"
//導(dǎo)入模型類(lèi)
#import "VideoModel.h"

//遵守代理協(xié)議
@interface ViewController ()<NSXMLParserDelegate>

//用于保存模型的數(shù)組
@property(nonatomic,strong)NSMutableArray <VideoModel *>*modelArr;

//用于臨時(shí)保存解析出來(lái)的數(shù)據(jù).
@property(nonatomic,strong)NSMutableString *mStr;

@end
  • NSXMLParse 類(lèi)進(jìn)行解析. 主要通過(guò)實(shí)現(xiàn)對(duì)象代理方法來(lái)解析.比較復(fù)雜.
@implementation ViewController


- (void)viewDidLoad {
    [super viewDidLoad];
    
    //URL 加載本地Apache服務(wù)器的數(shù)據(jù)解析
    NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos.xml"];
    
    //通過(guò)Session自動(dòng)開(kāi)啟線程進(jìn)行異步任務(wù).
    [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        //創(chuàng)建xml解析器
        NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
        //設(shè)置代理
        parser.delegate = self;
        //解析開(kāi)始
        [parser parse];
    }] resume];
    
}

//初始化可變數(shù)組
- (NSMutableArray *)modelArr
{
    if (_modelArr == nil) {
        _modelArr = [NSMutableArray array];
    }
    return _modelArr;
}

//初始化可變字符串
- (NSMutableString *)mStr
{
    if (_mStr == nil) {
        _mStr = [NSMutableString string];
    }
    return _mStr;
}
  • 真正用于解析的代理方法 只用這5個(gè),前后兩對(duì)方法,加上中間一個(gè)獲取數(shù)據(jù)的方法.
/**
 開(kāi)始解析
 */
- (void)parserDidStartDocument:(NSXMLParser *)parser;
{
    //這里只是開(kāi)始,貌似不用做什么
}

/**
 開(kāi)始一個(gè)新標(biāo)簽,這個(gè)時(shí)候應(yīng)該創(chuàng)建對(duì)應(yīng)的模型對(duì)象或者準(zhǔn)備為模型的屬性賦值.
 @param parser 解析器
 @param elementName 標(biāo)簽元素名字
 @param attributeDict 標(biāo)簽的屬性
 */
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName attributes:(NSDictionary<NSString *, NSString *> *)attributeDict;
{
    //看一下xml的結(jié)構(gòu),決定這里是干什么.對(duì)應(yīng)當(dāng)前的xml應(yīng)該是判斷后創(chuàng)建模型對(duì)象
    /*
     <videos>
     <video videoId="1">
     <name>01.C語(yǔ)言-語(yǔ)法預(yù)覽</name>
     <length>320</length>
     <videoURL>/itcast/videos/01.C語(yǔ)言-語(yǔ)法預(yù)覽.mp4</videoURL>
     <imageURL>/itcast/images/head1.png</imageURL>
     <desc>C語(yǔ)言-語(yǔ)法預(yù)覽</desc>
     <teacher>李雷</teacher>
     </video>
     */
    if ([elementName isEqualToString:@"video"]) {
        //創(chuàng)建新的模型對(duì)象
        VideoModel *model = [VideoModel new];
        //取出屬性,為videoId賦值
        model.videoId = @(attributeDict[@"videoId"].intValue);
        //將模型保存到數(shù)組
        [self.modelArr addObject:model];
    }
    
}



/**
 解析到標(biāo)簽中間的文字 標(biāo)簽中的文字不是一次性能讀完的,可能會(huì)分幾次調(diào)用這個(gè)方法,所以創(chuàng)建一個(gè)可變字符串保存起來(lái).

 @param parser 解析器
 @param string 文字
 */
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
{
    [self.mStr appendString:string];
}



/**
 解析到一個(gè)元素結(jié)束的地方.

 @param parser 解析器
 @param elementName 元素名字
 */
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName;
{
    //進(jìn)行判斷,外層標(biāo)簽不進(jìn)行KVC,否則崩潰.如果標(biāo)簽過(guò)多也可以重寫(xiě)model的方法 里面什么都不做就可以避免KVC報(bào)錯(cuò)
    //- (void)setValue:(id)value forUndefinedKey:(NSString *)key
    if (![elementName isEqualToString:@"video"] && ![elementName isEqualToString:@"videos"]) {
        //獲取當(dāng)前model
        VideoModel *model = self.modelArr.lastObject;
        //屬性的值就是解析出來(lái)的string,key則是標(biāo)簽的名字
        [model setValue:self.mStr forKey:elementName];
    }
    //最后對(duì)mStr進(jìn)行清空,準(zhǔn)備進(jìn)行下一個(gè)標(biāo)簽的解析
    self.mStr.string = @"";
}
/**
 結(jié)束解析
 */
- (void)parserDidEndDocument:(NSXMLParser *)parser;
{
    //所有標(biāo)簽解析完畢,打印數(shù)組看看是否轉(zhuǎn)換成功.
    NSLog(@"%@",self.modelArr);
}

@end

  • 當(dāng)然寫(xiě)完以后一定記得封裝到對(duì)應(yīng)的模型中,創(chuàng)建模型方法.那么在控制器中一句代碼就搞定了.
- (NSArray *)parserXML:(NSString *)URLString;
{
    //URL
    NSURL *url = [NSURL URLWithString:URLString];   
    //通過(guò)Session自動(dòng)開(kāi)啟線程進(jìn)行異步任務(wù).   
    [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        //創(chuàng)建xml解析器
        NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
        //設(shè)置代理
        parser.delegate = self;
        //解析開(kāi)始
        [parser parse];
    }] resume];
    
    return self.modelArr;
}
控制器一句話搞定加載

DOM解析

因?yàn)閕OS不能直接使用MAC的解析方式,所以DOM解析使用第三方框架.GDataXMLNode
它有增加刪除等方法,頭文件里面有對(duì)應(yīng)的方法,這里我們僅使用它來(lái)進(jìn)行XML的反序列化. 也就是解析

如果你不是通過(guò)控制臺(tái)中pod加載的框架

  • pod init
  • pod GDataXML-HTML
  • pod install

那么你可能會(huì)碰到引入框架后#import <libxml/tree.h>報(bào)錯(cuò)的問(wèn)題.

按照注釋,在project->build Settings ->Header Search PathsOther Linker Flags 中分別添加兩個(gè)地址

  1. /usr/include/libxml2
  2. -lxml2
  • 原文注釋
// libxml includes require that the target Header Search Paths contain
//
//   /usr/include/libxml2
//
// and Other Linker Flags contain
//
//   -lxml2
使用pod install 導(dǎo)入框架會(huì)自動(dòng)進(jìn)行配置,非常方便

DOM的解析有點(diǎn)類(lèi)似于字典轉(zhuǎn)模型的過(guò)程.根據(jù)解析的XML的結(jié)構(gòu)不同嵌套層次也不同.
由于整個(gè)解析過(guò)程比較連貫,所以直接復(fù)制粘貼整段代碼

  • 下面是全部代碼,注釋非常詳細(xì).
#import "VideoModel.h"

#import <GDataXMLNode.h>

@interface ViewController ()


@end

@implementation ViewController



- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    //1. 通過(guò)URL獲取XML的Data數(shù)據(jù).
    NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos.xml"];
    
    [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        //2. 獲取到Data數(shù)據(jù),創(chuàng)建GData對(duì)象 這里創(chuàng)建方法接收的是一個(gè)XML的string,所以先轉(zhuǎn)換Data成String
        NSString *xmlString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
        
        //這個(gè)方法可以進(jìn)去看看實(shí)現(xiàn),默認(rèn)是用UTF-8編碼
        GDataXMLDocument *GD = [[GDataXMLDocument alloc]initWithXMLString:xmlString error:nil];
        
        //3. 創(chuàng)建對(duì)象以后,取出根元素的子元素.返回的數(shù)組中都是GDataXMLElement
        NSArray *rootArr = GD.rootElement.children;
        
        //4. 那么現(xiàn)在要做的就是遍歷這個(gè)數(shù)組,對(duì)每一個(gè)元素進(jìn)行操作,轉(zhuǎn)換成模型了,這個(gè)過(guò)程類(lèi)似于字典轉(zhuǎn)模型.
        
        //創(chuàng)建一個(gè)可變數(shù)組保存轉(zhuǎn)換好的模型
        NSMutableArray *modelArrM = [NSMutableArray array];
        for (GDataXMLElement *element in rootArr) {
            
            
            // 1. 取出數(shù)組中的每一個(gè)元素后,先將這一組的video標(biāo)簽的屬性videoId取出來(lái)
            /*
             1. 屬性返回的是一個(gè)數(shù)組,我們的屬性只有一個(gè),所以取出第一個(gè).元素類(lèi)型是GDataXMLNode
             2. 通過(guò)這個(gè)結(jié)構(gòu)我們不難發(fā)現(xiàn),elment對(duì)應(yīng)的是一個(gè)樹(shù)枝節(jié)點(diǎn),它包含attributes數(shù)組,`屬性`一定是到頭了,是葉子節(jié)點(diǎn).所以這個(gè)數(shù)組中存的是多個(gè)GDataXMLNode類(lèi)型的元素.
             3. GDataXMLNode是GDataXMLElement的父類(lèi).node有對(duì)應(yīng)的方法name,stringValue.返回鍵值對(duì).
             */
            GDataXMLNode *node = element.attributes.firstObject;
            //NSLog(@"name = %@ value = %@",node.name,node.stringValue);
            
            //2. 創(chuàng)建一個(gè)模型將video標(biāo)簽的屬性保存 KVC
            VideoModel *model = [VideoModel new];
            [model setValue:node.stringValue forKey:node.name];
            
            //3. 取出video標(biāo)簽的子標(biāo)簽,數(shù)組,每個(gè)子元素依然是GDataXMLElement 代表著一個(gè)一個(gè)的標(biāo)簽.
            //NSLog(@"element.children = %@",[element.children.firstObject class]);
            
            //循環(huán)遍歷數(shù)組
            for (GDataXMLElement *elementTag in element.children) {
                //4. 這里的每一個(gè)tag就是最后的葉子借點(diǎn)了. name 是 key xml是value
                //NSLog(@"%@",elementTag);
                [model setValue:elementTag.XMLString forKey:elementTag.name];
                
            }
            
            //5. 將添加完元素的模型保存到數(shù)組
            [modelArrM addObject:model];
            
        }
        
        //轉(zhuǎn)換完畢,看看結(jié)果
        NSLog(@"%@",modelArrM);
        
    }] resume];
      
}
@end

轉(zhuǎn)換完畢.看看結(jié)果
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌魂爪,老刑警劉巖先舷,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異滓侍,居然都是意外死亡蒋川,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)撩笆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)捺球,“玉大人,你說(shuō)我怎么就攤上這事夕冲〉” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵歹鱼,是天一觀的道長(zhǎng)泣栈。 經(jīng)常有香客問(wèn)我,道長(zhǎng)弥姻,這世上最難降的妖魔是什么南片? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮庭敦,結(jié)果婚禮上疼进,老公的妹妹穿的比我還像新娘。我一直安慰自己秧廉,他們只是感情好伞广,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著定血,像睡著了一般赔癌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上澜沟,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天灾票,我揣著相機(jī)與錄音,去河邊找鬼茫虽。 笑死刊苍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的濒析。 我是一名探鬼主播正什,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼号杏!你這毒婦竟也來(lái)了婴氮?” 一聲冷哼從身側(cè)響起斯棒,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎主经,沒(méi)想到半個(gè)月后荣暮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡罩驻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年穗酥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惠遏。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡砾跃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出节吮,到底是詐尸還是另有隱情抽高,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布课锌,位于F島的核電站厨内,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏渺贤。R本人自食惡果不足惜雏胃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望志鞍。 院中可真熱鬧瞭亮,春花似錦、人聲如沸固棚。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)未玻。三九已至,卻和暖如春野芒,著一層夾襖步出監(jiān)牢的瞬間呜师,已是汗流浹背娶桦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汁汗,地道東北人衷畦。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像知牌,于是被迫代替她去往敵國(guó)和親祈争。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法角寸,類(lèi)相關(guān)的語(yǔ)法菩混,內(nèi)部類(lèi)的語(yǔ)法忿墅,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法沮峡,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,630評(píng)論 18 399
  • 1. XML總結(jié) 1.1. XML簡(jiǎn)介 XML : 可擴(kuò)展的標(biāo)記語(yǔ)言球匕。(和HTML非常類(lèi)似的) 可擴(kuò)展的。 自定義...
    Ethan_Walker閱讀 3,023評(píng)論 0 12
  • JSON JSON和XML都是需要解析的 JSON是一種輕量級(jí)的數(shù)據(jù)格式帖烘,一般用于數(shù)據(jù)交互服務(wù)器返回給客戶端的數(shù)據(jù)...
    JonesCxy閱讀 1,855評(píng)論 2 10
  • 一、緒論 上周工作需要了解項(xiàng)目的一些大體內(nèi)容橄杨,結(jié)果在xml解析這一塊看的迷迷糊糊的秘症,所以在這里把學(xué)習(xí)到xm...
    cao健強(qiáng)閱讀 4,035評(píng)論 1 7
  • 一、用戶指標(biāo) 新增用戶(新用戶):第一次啟動(dòng)應(yīng)用的用戶(以設(shè)備為判斷標(biāo)準(zhǔn))數(shù)式矫。 活躍用戶:?jiǎn)?dòng)過(guò)應(yīng)用的用戶(按照設(shè)...
    Smile竹_閱讀 939評(píng)論 0 1