XML解析
- 可扩展标记语言
- XML的特点,出身名门,W3C制定,微软和IBM曾经共同大力推荐过的数据格式
- XML 指可扩展标记语言(eXtensible Markup Language)
- 被设计用来传输和存储数据
- HTML 是设计用来表示页面的
SAX解析
SAX是iOS默认的解析XML的方式,
simple API for XML
. 是一种占用内存非常低,但是只能读取不能写入的解析方式.因为他是一行一行的解析的.
- 准备一个模型以及两个属性接收解析出来的数据.
#import "ViewController.h"
//导入模型类
#import "VideoModel.h"
//遵守代理协议
@interface ViewController ()
//用于保存模型的数组
@property(nonatomic,strong)NSMutableArray *modelArr;
//用于临时保存解析出来的数据.
@property(nonatomic,strong)NSMutableString *mStr;
@end
-
NSXMLParse
类进行解析. 主要通过实现对象代理方法来解析.比较复杂.
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//URL 加载本地Apache服务器的数据解析
NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos.xml"];
//通过Session自动开启线程进行异步任务.
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//创建xml解析器
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
//设置代理
parser.delegate = self;
//解析开始
[parser parse];
}] resume];
}
//初始化可变数组
- (NSMutableArray *)modelArr
{
if (_modelArr == nil) {
_modelArr = [NSMutableArray array];
}
return _modelArr;
}
//初始化可变字符串
- (NSMutableString *)mStr
{
if (_mStr == nil) {
_mStr = [NSMutableString string];
}
return _mStr;
}
- 真正用于解析的代理方法 只用这5个,前后两对方法,加上中间一个获取数据的方法.
/**
开始解析
*/
- (void)parserDidStartDocument:(NSXMLParser *)parser;
{
//这里只是开始,貌似不用做什么
}
/**
开始一个新标签,这个时候应该创建对应的模型对象或者准备为模型的属性赋值.
@param parser 解析器
@param elementName 标签元素名字
@param attributeDict 标签的属性
*/
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName attributes:(NSDictionary *)attributeDict;
{
//看一下xml的结构,决定这里是干什么.对应当前的xml应该是判断后创建模型对象
/*
*/
if ([elementName isEqualToString:@"video"]) {
//创建新的模型对象
VideoModel *model = [VideoModel new];
//取出属性,为videoId赋值
model.videoId = @(attributeDict[@"videoId"].intValue);
//将模型保存到数组
[self.modelArr addObject:model];
}
}
/**
解析到标签中间的文字 标签中的文字不是一次性能读完的,可能会分几次调用这个方法,所以创建一个可变字符串保存起来.
@param parser 解析器
@param string 文字
*/
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string;
{
[self.mStr appendString:string];
}
/**
解析到一个元素结束的地方.
@param parser 解析器
@param elementName 元素名字
*/
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName;
{
//进行判断,外层标签不进行KVC,否则崩溃.如果标签过多也可以重写model的方法 里面什么都不做就可以避免KVC报错
//- (void)setValue:(id)value forUndefinedKey:(NSString *)key
if (![elementName isEqualToString:@"video"] && ![elementName isEqualToString:@"videos"]) {
//获取当前model
VideoModel *model = self.modelArr.lastObject;
//属性的值就是解析出来的string,key则是标签的名字
[model setValue:self.mStr forKey:elementName];
}
//最后对mStr进行清空,准备进行下一个标签的解析
self.mStr.string = @"";
}
/**
结束解析
*/
- (void)parserDidEndDocument:(NSXMLParser *)parser;
{
//所有标签解析完毕,打印数组看看是否转换成功.
NSLog(@"%@",self.modelArr);
}
@end
- 当然写完以后一定记得封装到对应的模型中,创建模型方法.那么在控制器中一句代码就搞定了.
- (NSArray *)parserXML:(NSString *)URLString;
{
//URL
NSURL *url = [NSURL URLWithString:URLString];
//通过Session自动开启线程进行异步任务.
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//创建xml解析器
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
//设置代理
parser.delegate = self;
//解析开始
[parser parse];
}] resume];
return self.modelArr;
}
DOM解析
因为iOS不能直接使用MAC的解析方式,所以DOM解析使用第三方框架.
GDataXMLNode
它有增加删除等方法,头文件里面有对应的方法,这里我们仅使用它来进行XML的反序列化. 也就是解析
如果你不是通过控制台中pod
加载的框架
- pod init
- pod
GDataXML-HTML
- pod install
那么你可能会碰到引入框架后#import
报错的问题.
按照注释,在project->build Settings ->
Header Search Paths
和Other Linker Flags
中分别添加两个地址
/usr/include/libxml2
-lxml2
- 原文注释
// libxml includes require that the target Header Search Paths contain
//
// /usr/include/libxml2
//
// and Other Linker Flags contain
//
// -lxml2
DOM的解析有点类似于字典转模型的过程.根据解析的XML的结构不同嵌套层次也不同.
由于整个解析过程比较连贯,所以直接复制粘贴整段代码
- 下面是全部代码,注释非常详细.
#import "VideoModel.h"
#import
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1. 通过URL获取XML的Data数据.
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数据,创建GData对象 这里创建方法接收的是一个XML的string,所以先转换Data成String
NSString *xmlString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
//这个方法可以进去看看实现,默认是用UTF-8编码
GDataXMLDocument *GD = [[GDataXMLDocument alloc]initWithXMLString:xmlString error:nil];
//3. 创建对象以后,取出根元素的子元素.返回的数组中都是GDataXMLElement
NSArray *rootArr = GD.rootElement.children;
//4. 那么现在要做的就是遍历这个数组,对每一个元素进行操作,转换成模型了,这个过程类似于字典转模型.
//创建一个可变数组保存转换好的模型
NSMutableArray *modelArrM = [NSMutableArray array];
for (GDataXMLElement *element in rootArr) {
// 1. 取出数组中的每一个元素后,先将这一组的video标签的属性videoId取出来
/*
1. 属性返回的是一个数组,我们的属性只有一个,所以取出第一个.元素类型是GDataXMLNode
2. 通过这个结构我们不难发现,elment对应的是一个树枝节点,它包含attributes数组,`属性`一定是到头了,是叶子节点.所以这个数组中存的是多个GDataXMLNode类型的元素.
3. GDataXMLNode是GDataXMLElement的父类.node有对应的方法name,stringValue.返回键值对.
*/
GDataXMLNode *node = element.attributes.firstObject;
//NSLog(@"name = %@ value = %@",node.name,node.stringValue);
//2. 创建一个模型将video标签的属性保存 KVC
VideoModel *model = [VideoModel new];
[model setValue:node.stringValue forKey:node.name];
//3. 取出video标签的子标签,数组,每个子元素依然是GDataXMLElement 代表着一个一个的标签.
//NSLog(@"element.children = %@",[element.children.firstObject class]);
//循环遍历数组
for (GDataXMLElement *elementTag in element.children) {
//4. 这里的每一个tag就是最后的叶子借点了. name 是 key xml是value
//NSLog(@"%@",elementTag);
[model setValue:elementTag.XMLString forKey:elementTag.name];
}
//5. 将添加完元素的模型保存到数组
[modelArrM addObject:model];
}
//转换完毕,看看结果
NSLog(@"%@",modelArrM);
}] resume];
}
@end