在“元素标记读取”事件( 即endElementLocalName 方法)中,则进行标志变量的改变/归零。
- #import <Foundation/Foundation.h>
- #import <libxml/tree.h>
- #import "BaseXmlParser.h"
- @interface DLTLoginParser : BaseXmlParser {
- int flag;
- NSMutableDictionary* _currentItem;
- }
- - (void)startElementLocalName:(const xmlChar*)localname
- prefix:(const xmlChar*)prefix
- URI:(const xmlChar*)URI
- nb_namespaces:(int)nb_namespaces
- namespaces:(const xmlChar**)namespaces
- nb_attributes:(int)nb_attributes
- nb_defaulted:(int)nb_defaultedslo
- attributes:(const xmlChar**)attributes;
- - (void):(const xmlChar*)localname
- prefix:(const xmlChar*)prefix URI:(const xmlChar*)URI;
- - (void)charactersFound:(const xmlChar*)ch
- len:(int)len;
- @end
- #import "DLTLoginParser.h"
- @implementation DLTLoginParser
- -(id)init{
- if(self=[super init]){
- NSMutableArray* items=[[NSMutableArray alloc]init];
- [_root setObject:items forKey:@"items"];
- [items release];//已被_root持有了,可以释放
- }
- return self;
- }
- -(void)dealloc{
- [_currentItem release],_currentItem=nil;
- [super dealloc];
- }
- //--------------------------------------------------------------//
- #pragma mark -- libxml handler,主要是3个回调方法--
- //--------------------------------------------------------------//
- //解析元素开始标记时触发,在这里取元素的属性值
- - (void)startElementLocalName:(const xmlChar*)localname
- prefix:(const xmlChar*)prefix
- URI:(const xmlChar*)URI
- nb_namespaces:(int)nb_namespaces
- namespaces:(const xmlChar**)namespaces
- nb_attributes:(int)nb_attributes
- nb_defaulted:(int)nb_defaultedslo
- attributes:(const xmlChar**)attributes
- {
- // login_status,置标志为1
- if (strncmp((char*)localname, "login_status", sizeof("login_status")) == 0) {
- flag=1;
- return;
- }
- // system,置标志为2
- if (strncmp((char*)localname, "system", sizeof("system")) == 0) {
- flag=2;
- _currentItem = [NSMutableDictionary dictionary];
- //查找属性
- NSString *key,*val;
- for (int i=0; i<nb_attributes; i++){
- key = [NSString stringWithCString:(const char*)attributes[0] encoding:NSUTF8StringEncoding];
- val = [[NSString alloc] initWithBytes:(const void*)attributes[3] length:(attributes[4] - attributes[3])
- encoding:NSUTF8StringEncoding];
- NSLog(@"key=%@,val=%@",key,val);
- if ([@"Name" isEqualToString:key]) {
- [_currentItem setObject:val forKey:@"name"];
- break;
- }
- // [val release];
- attributes += 5;//指针移动5个字符串,到下一个属性
- }
- [[_root objectForKey:@"items"] addObject:_currentItem];
- return;
- }
- }
- //解析元素结束标记时触发
- - (void)endElementLocalName:(const xmlChar*)localname
- prefix:(const xmlChar*)prefix URI:(const xmlChar*)URI
- {
- flag=0;//标志归零
- }
- //解析元素体时触发
- - (void)charactersFound:(const xmlChar*)ch
- len:(int)len
- {
- // 取login_status元素体
- if (flag==1) {
- NSString* string;
- string = [[NSString alloc] initWithBytes:ch length:len encoding:NSUTF8StringEncoding];
- [_root setObject:string forKey:@"login_status"];
- NSLog(@"login_status:%@",string);
- }
- }
- @end
接下来,改造我们的异步请求操作类URLOperation。首先在interface中增加
两个变量:
- xmlParserCtxtPtr _parserContext; //Xml解析器指针
- BaseXmlParser* baseParser; //Xml解析器
其中第1个变量(一个结构体)的声明显得有点奇怪,似乎是跟第2个变量混淆了。这是因为libxml是一个c函数库,其函数调用仍然使用一种面向结构的编程风格。所以我们在后面还会看到一些结构体似的变量。
另外,把_data成员的类型从NSMutableData改变为NSMutableDictionary,并把它配置为属性,因为我们的请求结果应当被xml解析器解析为dictionary了:
- @property (nonatomic,retain) NSDictionary *data;
当然,记住为它提供访问方法:
- @synthesize data=_data;
然后,更改 initWithURLString 构造方法,为其增加一个名为 xmlParser 的参数
- - (id)initWithURLString:(NSString *)url xmlParser:(BaseXmlParser*)parser{
- if (self = [super init]) {
- baseParser=[parser retain];
- NSLog(@"%@",url);
- _request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];//[[NSURLRequest requestWithURL:[NSURL URLWithString:url]]retain];
- //构建gb2312的encoding
- enc =CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
- _data = [[NSMutableData data] retain];
- }
- return self;
- }
在start方法中,我们可以这样创建一个xml解析器指针:
// 创建XML解析器指针
- _parserContext = xmlCreatePushParserCtxt(&_saxHandlerStruct, baseParser, NULL, 0, NULL);
注意第2个参数就是具体实现了sax解析的xml解析器。这个解析器对象是通过构造函数“注入”的。
而第一个参数是一个结构体指针 xmlSAXHandler 结构体,这个结构体我们定义为静态变量(注意把定义放在@implementation⋯⋯@end之外):
//libxml的xmlSAXHandler结构体定义,凡是要实现的handler函数都写在这里,不准备实现的用null代替。一般而言,我们只实现其中3个就够了
- static xmlSAXHandler _saxHandlerStruct = {
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- charactersFoundHandler,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- XML_SAX2_MAGIC,
- NULL,
- startElementHandler,
- endElementHandler,
- NULL,
- };
机构体中填入了我们准备实现的3个方法句柄,因此我们还应当定义这3个方法。由于结构体是静态的,只能访问静态成员,所以这3个方法也是静态的:
- //3个静态方法的实现,其实是调用了参数ctx的成员方法, ctx在_parserContext初始化时传入
- static void startElementHandler(
- void* ctx,
- const xmlChar* localname,
- const xmlChar* prefix,
- const xmlChar* URI,
- int nb_namespaces,
- const xmlChar** namespaces,
- int nb_attributes,
- int nb_defaulted,
- const xmlChar** attributes)
- {
- [(BaseXmlParser*)ctx
- startElementLocalName:localname
- prefix:prefix URI:URI
- nb_namespaces:nb_namespaces
- namespaces:namespaces
- nb_attributes:nb_attributes
- nb_defaulted:nb_defaulted
- attributes:attributes];
- }
- static void endElementHandler(
- void* ctx,
- const xmlChar* localname,
- const xmlChar* prefix,
- const xmlChar* URI)
- {
- [(BaseXmlParser*)ctx
- endElementLocalName:localname
- prefix:prefix
- URI:URI];
- }
- static void charactersFoundHandler(
- void* ctx,
- const xmlChar* ch,
- int len)
- {
- [(BaseXmlParser*)ctx
- charactersFound:ch len:len];
- }
其实这3个静态方法只是调用了超类BaseXmlParser的成员方法,他的具体类型依赖于ctx的注入类型,也就是说,这里的ctx可以是任何BaseXmlParser的子类。 实际使用中,我们应该注入其子类,从而可以根据不同的情况为URLOperation“注入”不同的解析器,实现解析不同的xml文件的目的。
现在,需要把解析器应用到NSURLConnection的委托方法中(这里省略了部分代码,只列出了新增加的部分):
- #pragma mark NSURLConnection delegate Method
- // 接收到数据(增量)时
- - (void)connection:(NSURLConnection*)connection
- didReceiveData:(NSData*)data {
- // 使用libxml解析器进行xml解析
- xmlParseChunk(_parserContext, (const char*)[data bytes], [data length], 0);
- ⋯⋯
- }
- // HTTP请求结束时
- - (void)connectionDidFinishLoading:(NSURLConnection*)connection {
- if(baseParser!=nil && baseParser!=NULL){
- [self setData:[[NSDictionary alloc] initWithDictionary:[baseParser getResult]]];
- }else {
- NSLog(@"baseparser is nil");
- }
- // 添加解析数据(结束),注意最后一个参数termindate
- xmlParseChunk(_parserContext, NULL, 0, 1);
- // 释放XML解析器
- if (_parserContext) {
- xmlFreeParserCtxt(_parserContext), _parserContext = NULL;
- }
- ⋯⋯
- }
- -(void)connection: (NSURLConnection *) connection didFailWithError: (NSError *) error{
- // 释放XML解析器
- if (_parserContext) {
- xmlFreeParserCtxt(_parserContext), _parserContext = NULL;
- }
- ⋯⋯
- }
- @end
接下来,在“登录”按钮中代码也要做相应的修改,因为URLOperation的构造函数要求传递一个具体的xml解析器对象:
- //构造xmlparser
- DLTLoginParser* parser=[[DLTLoginParser alloc]init];
- URLOperation* operation=[[URLOperation alloc ]initWithURLString:url xmlParser:parser];
- [parser release];
然后,在接收变更通知方法中打印解析结果:
- URLOperation* ctx=(URLOperation*)context;
- NSLog(@"%@",[ctx data]);
后台打印结果:
- {
- items = (
- {
- name = "\U4e91\U7535\U4f01\U4fe1\U901a";
- },
- {
- name = "\U79fb\U52a8\U8c03\U5ea6";
- },
- {
- name = "\U79fb\U52a8\U62a2\U4fee";
- }
- );
- "login_status" = true;
- }
小结:深度解析Cocoa异步请求和libxml2.dylib教程的内容介绍完了,希望本文对你有所帮助!