iOS 开发 网络编程与通信之XML解析

本文介绍了iOS中XML文件的解析方式,包括SAX和DOM解析,并详细阐述了XML解析过程,从发送请求到解析响应,以及利用NSXMLParser进行解析的代理方法。在实际操作中,需要注意节点内容的拼接以及模型对象的创建和赋值,以避免数据重复拼接的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

XML文件解析方式

  • SAX方式解析XML
    苹果官方提供的原生的解析XML文件的方式
    在iOS上解析XML文件的方式.
    速度快,内存占用小.
    是只读的,只能读取XML文件数据不能做修改操作.

  • DOM方式解析XML
    是在MAC使用的解析方式.
    注意 : 内存消耗极大,不适用于手机.
    可以读写XML文件.
    iPhone无法直接使用DOM方式解析XML.
    如果要在iPhone上使用DOM方式解析XML文件,可以使用第三方框架.

解析XML过程

//发送请求
1.获取URL–>创建请求–>发送请求–>响应处理–>XML解析
//xml解析器
2>创建XML解析器–>设置解析器代理–>开启解析器
//代理方法
3>开启–>开始节点–>节点内容–>结束节点–>结束–>监听错误信息
//模型(开始节点–节点内容–结束节点)
4>判断开始节点–创建模型–标签属性赋值给模型–模型添加到数组 //可遍数组
5>保存节点内容的可变字符串 //可变字符串
6>每次取出属性数组的模型–判断对应节点–给模型属性赋值–可变字符串置为@“”

NSXMLParser–parse–DidStartDocument–didStartElement–foundCharacters–didEndElement–DidEndDocument–parseErrorOccurred

实例XML解析

//
//  ViewController.m
//  XML解析之SAX
//
//  Created by Apple on 14/7/5.
//  Copyright © 2014年 mac. All rights reserved.
//

#import "ViewController.h"
#import "VideoModel.h"

@interface ViewController () <NSXMLParserDelegate>

@property (nonatomic, strong) NSMutableArray *videoM;
@property (nonatomic, copy) NSMutableString *stringM;
@property (nonatomic, strong) VideoModel *currentModel;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self loadXML];
}

- (void)loadXML {
    // URL
    NSURL *URL= [NSURL URLWithString:@"http://localhost/videos.xml"];
    
    // 请求
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    
    // 发送请求
    [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        // 处理响应
        if (connectionError == nil && data != nil) {
            
            // 反序列化 : 解析XML文件的二进制
            // 1.创建XML解析器
            NSXMLParser *XMLParser = [[NSXMLParser alloc] initWithData:data];
            // 2.设置xml解析器的代理
            XMLParser.delegate = self;
            // 3.启动解析器
            [XMLParser parse];
            
        } else {
            NSLog(@"%@",connectionError);
        }
    }];
}

#pragma mark - NSXMLParserDelegate
/*
 1.VideoModel 什么时候创建?
 2.模型对象的videoId属性什么时候赋值?
 3.什么时候把创建的所有的模型对象添加到数组中?
 4.什么时候给模型对象的其他属性赋值?
 5.模型对象属性赋值的值在哪里?
 */

// 1.开始解析XML文档
- (void)parserDidStartDocument:(NSXMLParser *)parser {
    NSLog(@"1.开始解析XML文档");
}

// 2.找开始节点/标签/元素
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName attributes:(NSDictionary<NSString *, NSString *> *)attributeDict {
    // elementName : 开始节点
    // attributeDict : 节点的属性
//    NSLog(@"2.找开始节点 = %@--%@",elementName,attributeDict);
    
    // 一旦找到video节点就需要建立一个模型对象
    if ([elementName isEqualToString:@"video"]) {
        // 创建video节点对应的模型对象
        VideoModel *model = [[VideoModel alloc] init];
        
        // 给模型对象的videoId属性赋值 : 因为只有在这里可以拿到videoId(节点属性)
        model.videoId = attributeDict[@"videoId"];
        
        // 把创建好的模型对象添加到模型数组 : 简单,在哪里创建的就在哪里添加
        [self.videoM addObject:model];
    }
}

// 3.找节点之间的类容
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    // string : 节点之间的内容
//    NSLog(@"3.找节点之间的类容 = %@",string);
    
    // 循环拼接节点之间的内容
    [self.stringM appendString:string];
}

// 4.找结束节点
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName {
    // elementName : 结束节点
//    NSLog(@"4.找结束节点 = %@",elementName);
    
    // 在找到结束节点之后,给模型对象的其他属性赋值
    // 从数组里面取最后一个元素(模型对象)
    VideoModel *model = [self.videoM lastObject];
    // 给模型对象的其他属性赋值,但是videos节点和video节点是不需要赋值的
    if ([elementName isEqualToString:@"videos"] || [elementName isEqualToString:@"video"]) {
        return;
    }
    
    // 只有排除了videos节点和video节点,才需要赋值
    [model setValue:self.stringM forKey:elementName];
    
    // 使用完了之后要把可变字符串清空,以免再次拼接时,把前面的内容也拼接进去了

    // 这是把不可变字符串赋值给可变字符串,不被允许的
//    self.stringM = @"";
    
    // 清空之后,stringM下次再用的时候,又要懒加载创建,性能差了一点
//    self.stringM = nil;
    
    [self.stringM setString:@""];
}

// 5.结束解析文档
- (void)parserDidEndDocument:(NSXMLParser *)parser {
    NSLog(@"5.结束解析文档");
    NSLog(@"%@",self.videoM);
}

// 6.监听XML文件是否解析出错
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
    NSLog(@"6.监听XML文件是否解析出错 = %@",parseError);
}

- (NSMutableArray *)videoM {
    if (_videoM == nil) {
        _videoM = [NSMutableArray array];
    }
    return _videoM;
}

- (NSMutableString *)stringM {
    if (_stringM == nil) {
        _stringM = [NSMutableString string];
    }
    return _stringM;
}

/*
 全局的模型对象不能懒加载,如果懒加载之后,当前类有且只有一个model
 */
//- (VideoModel *)currentModel
//{
//    if (_currentModel == nil) {
//        _currentModel = [[VideoModel alloc] init];
//    }
//    return _currentModel;
//}

@end

这里写图片描述
这里写图片描述

  • 通过代理方法中的NSLog,可以了解到解析XML文件中数据的过程.

  • 以上步骤,2—>3—>4,会不断循环,一直到所有数据解析完成.

  • 节点之间的内容并不是一次就能找全的,有时候需要循环多次才能找全.

  • 如果解析数据量非常大的XML文件是很耗时的.当在哪个线程设置了代理协议,就在哪个线程中解析.

  • 不能按照字典转模型的逻辑来处理XML转模型.解析XML数据的过程中没有字典.

  • 每个video标签就是描述一个视频的.每个模型对象也是用来描述一个视频的.所以每找到一个video标签就要创建一个对应的模型对象.模型对象属性赋值会在多个代理方法中进行赋值.所以需要定义成全局的模型对象.

  • 节点之间的内容有时候不会一次性就找全,所以需要多次拼接字符串,而且要在找结束节点代理方法中使用,所以也要定义成全局的可变字符串.

  • 为了能够打印出所有的模型对象,需要将模型对象添加到数组中.

  • 在拼接下一个节点之间的内容之前,给模型属性付完值之后,将供拼接的可变字符串清空.

注意 : 多次拼接完节点之间的内容,且在找结束节点代理方法中,给模型属性赋值完成之后,需要清空这个用于拼接的可变字符串.否则,会c重复拼接字符串,即将上一个节点之间的内容继续拼接到下一个节点之间的内容前面.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值