iOS开发------XML原生解析(NSXMLParser篇)

由于项目需要,最近就研究了一些与视频流相关的知识,在学习的过程中发现,JSON作为轻量级的数据传输格式就显的非常不便.当然,这句话的意思就是说在学习过程中碰到的XML格式数据居多了呗,这时掌握一些XML的解析方法就显得重要了。

尽管之前转发过一篇介绍XML解析的博文网络数据的XML解析,但总觉得千转不如一写,所以就花时间研究了一下。

很多第三方代码其实能够很好的帮助我们解析数据,但由于楼主的个性,在认可第三方代码的同时,还是要掌握一下它的原理的(当然这个原理,只是比较肤浅的原理了),这一篇就记录一下iOS解析XML的原生类NSXMLParser.

本人用的是Mac自带的本地服务器,将php文件放置服务器文件夹即可,XML.php文件内容如下:

<?php

//声明一个XML文本,并声明版本和编码
$xmlParse = new DOMDocument('1.0','utf-8');
$xmlParse->formatOutput = true;

//创建头结点
$rootElement = $xmlParse->createElement("Persons");
$xmlParse->appendChild($rootElement);


//添加测试数据-添加三组数据
for ($i=0; $i < 3; $i++) 
{ 
    $element = $xmlParse->createElement("Person");

    //姓名
    $name = $xmlParse->createElement("name");
    $name->appendChild($xmlParse->createTextNode("RITL".$i));//生成结点内容
    $element->appendChild($name);

    //年龄
    $age = $xmlParse->createElement("age");
    $age->appendChild($xmlParse->createTextNode($i * 3));
    $element->appendChild($age);

    //添加该结点
    $rootElement->appendChild($element);
}

//返回
echo $xmlParse->saveXML();

?>


为了比较直观,全部用的iOS原生框架(当然,也没有使用第三方的必要0.0)
下面是用到的所有延展属性:

@import Foundation;

@interface ViewController ()<NSXMLParserDelegate>

/// @brief 存储xml对象的字典
@property (nonatomic, strong)NSMutableArray * persons;

/// @brief 记录当前编辑的xml结点对象
@property (nonatomic, strong)NSMutableDictionary * currentDictionary;

/// @brief 记录当前编辑的xml结点对象的key值
@property (nonatomic, copy)NSString * currentDictionaryKey;

@end


在ViewDidLoad中进行网络请求以及使用方法解析收到的XML信息:

- (void)viewDidLoad {
    [super viewDidLoad];

    //拼接字符串路径
    NSURL * requestURL = [NSURL URLWithString:@"http://localhost/XML.php"];

    //网络请求对象
    NSURLSession * session = [NSURLSession sharedSession];

    //避免强引用,属性值需要变化,用__block修饰
    __block __weak typeof(self) copy_self = self;

    //初始化请求任务
    NSURLSessionDataTask * dataTask = [session dataTaskWithURL:requestURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        //获得请求返回的xml字符串
        NSString * dataString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];

        NSLog(@"xml = %@",dataString);

        //进行解析
        [copy_self decodeXML:dataString];

        //打印出结果:
        NSLog(@"result = %@",copy_self.persons);

    }];

    //开始请求
    [dataTask resume];
}


这里打印的xml格式为:

2016-06-02 15:32:03.773 YXMLParse[1069:27997] xml = <?xml version="1.0" encoding="utf-8"?>
<Persons>
  <Person>
    <name>RITL0</name>
    <age>0</age>
  </Person>
  <Person>
    <name>RITL1</name>
    <age>3</age>
  </Person>
  <Person>
    <name>RITL2</name>
    <age>6</age>
  </Person>
</Persons>


本博文的干货开始,首先是解析XML字符串的解析方法:decodeXML::

/**
 *  解析XML字符串
 *
 *  @param xmlString 解析的XML字符串
 *
 *  @return 是否解析成功,true表示成功,false表示失败
 */
- (BOOL)decodeXML:(NSString *)xmlString
{
    //初始化NSXMLParse对象
    NSXMLParser * xmlParse = [[NSXMLParser alloc]initWithData:[xmlString dataUsingEncoding:NSUTF8StringEncoding]];

    //设置代理
    xmlParse.delegate = self;

    //开始解析
    return [xmlParse parse];
}


通过协议方法不断的进行解析,同时我也按照自己的理解,尽可能的进行了注释:

#pragma mark - *************** <NSXMLParserDelegate>

/**
 *  开始解析进行回调的Delegate Function
 *
 *  @param parser 执行回调方法的NSXMLParser方法
 */
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
    //初始化存放xml数据字典的数组
    self.persons = [NSMutableArray arrayWithCapacity:0];
}
/**
 *  准备解析结点进行的回调
 *  在此处可以获取每个xml结点所传递的信息,如(xmlns--类似命名空间)
 *
 *  @param parser        执行回调方法的NSXMLParser对象
 *  @param elementName   结点的字符串描述(如name..)
 *  @param namespaceURI  命名空间的统一资源标志符字符串描述
 *  @param qName         命名空间的字符串描述
 *  @param attributeDict 参数字典
 */
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
  namespaceURI:(nullable NSString *)namespaceURI
 qualifiedName:(nullable NSString *)qName attributes:(NSDictionary<NSString *, NSString *> *)attributeDict
{
    //表示开始遍历子结点了
    if ([elementName isEqualToString:@"Person"])//对Person标签进行比对
    {
        //新建一个索引为elementName的key字典
        NSMutableDictionary * elementNameDictionary = [NSMutableDictionary dictionary];

        //将数据添加到当前数组
        [self.persons addObject:elementNameDictionary];

        //记录当前进行操作的结点字典
        self.currentDictionary = elementNameDictionary;
    }

    //表示不是Persons的子节点,因为是else if,也保证
    else if (![elementName isEqualToString:@"Persons"])//排除Person以及Persons标签
    {
        //记录当前的结点值
        self.currentDictionaryKey = elementName;

        //设置当前element的值,先附初始值
        [self.currentDictionary addEntriesFromDictionary:@{elementName:@""}];
    }
}
/**
 *  获得首尾结点间内容信息的内容
 *
 *  @param parser 执行回调方法的NSXMLParse对象
 *  @param string 结点间的内容
 *  如果结点之间的内容是结点段,那么返回的string首字符为unichar类型的‘\n’
 *  如果不是结点段,那么直接返回之间的信息内容
 */
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    //如果结点内容的第一个字符是‘\n’,表示是包含其他结点的结点
    if (string.length > 0 && [string characterAtIndex:0] != '\n')
    {
        //对当前字典进行初始化赋值
        [self.currentDictionary setValue:string forKey:self.currentDictionaryKey];
    }   
}
/**
 *  某个结点解析完毕进行的回调
 *
 *  @param parser       执行回调方法的NSXMLParse对象
 *  @param elementName  结点的字符串描述(如name..)
 *  @param namespaceURI 命名空间的统一资源标志符字符串描述
 *  @param qName        命名空间的字符串描述
 */
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    //只有结束Person的时候清除相关数据
    if ([elementName isEqualToString:@"Person"])
    {
        //cleat data
        self.currentDictionary = nil;
        self.currentDictionaryKey = nil;
    }
}
/**
 *  XML解析完成进行的回调
 *
 *  @param parser 执行回调方法的NSXMLParse对象
 */
- (void)parserDidEndDocument:(NSXMLParser *)parser
{

}

最后输出一下解析完毕之后添加到数组中的数据,打印如下:

2016-06-02 15:32:03.774 YXMLParse[1069:27997] result = (
        {
        age = 0;
        name = RITL0;
    },
        {
        age = 3;
        name = RITL1;
    },
        {
        age = 6;
        name = RITL2;
    }
)//完成解析
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值