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 Paths
和Other Linker Flags
中分別添加兩個(gè)地址
/usr/include/libxml2
-lxml2
- 原文注釋
// libxml includes require that the target Header Search Paths contain
//
// /usr/include/libxml2
//
// and Other Linker Flags contain
//
// -lxml2
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