深度解析Cocoa异步请求和libxml2.dylib教程(3)

本文详细介绍了如何利用Cocoa异步请求框架和libxml2.dylib库进行XML文件解析,包括XML解析器的创建、配置、应用到NSURLConnection委托方法中的流程,以及解析过程中的关键方法实现。

在“元素标记读取”事件( 即endElementLocalName 方法)中,则进行标志变量的改变/归零。

  1. #import <Foundation/Foundation.h> 
  2.  
  3. #import <libxml/tree.h> 
  4.  
  5. #import "BaseXmlParser.h"  
  6.  
  7. @interface DLTLoginParser : BaseXmlParser {  
  8.  
  9.     int flag;  
  10.  
  11.     NSMutableDictionary*    _currentItem;    
  12.  
  13. }
  14.  
  15. - (void)startElementLocalName:(const xmlChar*)localname  
  16.                        prefix:(const xmlChar*)prefix  
  17.                           URI:(const xmlChar*)URI  
  18.                 nb_namespaces:(int)nb_namespaces  
  19.                    namespaces:(const xmlChar**)namespaces  
  20.                 nb_attributes:(int)nb_attributes  
  21.                 nb_defaulted:(int)nb_defaultedslo   
  22.                    attributes:(const xmlChar**)attributes;  
  23.  
  24. - (void):(const xmlChar*)localname  
  25.  
  26.                      prefix:(const xmlChar*)prefix URI:(const xmlChar*)URI;  
  27.  
  28. - (void)charactersFound:(const xmlChar*)ch  
  29.  
  30.                     len:(int)len;  
  31.  
  32. @end  
  33. #import "DLTLoginParser.h"  
  34. @implementation DLTLoginParser  
  35. -(id)init{  
  36.     if(self=[super init]){  
  37.         NSMutableArray* items=[[NSMutableArray alloc]init];
  38.         [_root setObject:items forKey:@"items"];  
  39.         [items release];//已被_root持有了,可以释放
  40.     }
  41.     return self;  
  42. }  
  43.  
  44. -(void)dealloc{  
  45.     [_currentItem release],_currentItem=nil;  
  46.     [super dealloc];  
  47. }  
  48.  
  49. //--------------------------------------------------------------//  
  50.  
  51. #pragma mark -- libxml handler,主要是3个回调方法--  
  52.  
  53. //--------------------------------------------------------------//  
  54.  
  55. //解析元素开始标记时触发,在这里取元素的属性值  
  56. - (void)startElementLocalName:(const xmlChar*)localname  
  57.                        prefix:(const xmlChar*)prefix  
  58.                           URI:(const xmlChar*)URI  
  59.                 nb_namespaces:(int)nb_namespaces  
  60.                    namespaces:(const xmlChar**)namespaces  
  61.                 nb_attributes:(int)nb_attributes  
  62.                  nb_defaulted:(int)nb_defaultedslo  
  63.                    attributes:(const xmlChar**)attributes  
  64. {  
  65.     // login_status,置标志为1  
  66.     if (strncmp((char*)localname, "login_status", sizeof("login_status")) == 0) {  
  67.         flag=1;  
  68.         return;  
  69.     }  
  70.     // system,置标志为2  
  71.     if (strncmp((char*)localname, "system", sizeof("system")) == 0) {  
  72.         flag=2;  
  73.         _currentItem = [NSMutableDictionary dictionary];  
  74.         //查找属性  
  75.         NSString *key,*val;  
  76.         for (int i=0; i<nb_attributes; i++){  
  77.             key = [NSString stringWithCString:(const char*)attributes[0] encoding:NSUTF8StringEncoding];  
  78.             val = [[NSString alloc] initWithBytes:(const void*)attributes[3] length:(attributes[4] - attributes[3]) 
  79. encoding:NSUTF8StringEncoding];  
  80.             NSLog(@"key=%@,val=%@",key,val);  
  81.             if ([@"Name" isEqualToString:key]) {  
  82.                 [_currentItem setObject:val forKey:@"name"];  
  83.                 break;  
  84.             }  
  85.             // [val release];  
  86.             attributes += 5;//指针移动5个字符串,到下一个属性  
  87.         }  
  88.         [[_root objectForKey:@"items"] addObject:_currentItem];  
  89.         return;  
  90.     }  
  91. }  
  92. //解析元素结束标记时触发  
  93. - (void)endElementLocalName:(const xmlChar*)localname  
  94.                      prefix:(const xmlChar*)prefix URI:(const xmlChar*)URI  
  95. {  
  96.     flag=0;//标志归零  
  97. }  
  98. //解析元素体时触发  
  99. - (void)charactersFound:(const xmlChar*)ch  
  100.                     len:(int)len  
  101. {  
  102.     // 取login_status元素体  
  103.     if (flag==1) {  
  104.         NSString*   string;  
  105.         string = [[NSString alloc] initWithBytes:ch length:len encoding:NSUTF8StringEncoding];  
  106.         [_root setObject:string forKey:@"login_status"];  
  107.         NSLog(@"login_status:%@",string);  
  108.     }  
  109. }  
  110. @end 

接下来,改造我们的异步请求操作类URLOperation。首先在interface中增加

两个变量:

  1. xmlParserCtxtPtr  _parserContext; //Xml解析器指针  
  2. BaseXmlParser* baseParser; //Xml解析器 

其中第1个变量(一个结构体)的声明显得有点奇怪,似乎是跟第2个变量混淆了。这是因为libxml是一个c函数库,其函数调用仍然使用一种面向结构的编程风格。所以我们在后面还会看到一些结构体似的变量。

另外,把_data成员的类型从NSMutableData改变为NSMutableDictionary,并把它配置为属性,因为我们的请求结果应当被xml解析器解析为dictionary了:

  1. @property (nonatomic,retain) NSDictionary *data; 

当然,记住为它提供访问方法:

  1. @synthesize data=_data

然后,更改 initWithURLString 构造方法,为其增加一个名为 xmlParser 的参数

  1. - (id)initWithURLString:(NSString *)url xmlParser:(BaseXmlParser*)parser{  
  2.     if (self = [super init]) {  
  3.         baseParser=[parser retain];  
  4.         NSLog(@"%@",url);  
  5.         _request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];//[[NSURLRequest requestWithURL:[NSURL URLWithString:url]]retain];  
  6.         //构建gb2312的encoding  
  7.         enc =CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);  
  8.         _data = [[NSMutableData data] retain];  
  9.     }  
  10.     return self;  

在start方法中,我们可以这样创建一个xml解析器指针:

// 创建XML解析器指针

  1. _parserContext = xmlCreatePushParserCtxt(&_saxHandlerStruct, baseParser, NULL, 0, NULL); 

注意第2个参数就是具体实现了sax解析的xml解析器。这个解析器对象是通过构造函数“注入”的。

而第一个参数是一个结构体指针 xmlSAXHandler 结构体,这个结构体我们定义为静态变量(注意把定义放在@implementation⋯⋯@end之外):

//libxml的xmlSAXHandler结构体定义,凡是要实现的handler函数都写在这里,不准备实现的用null代替。一般而言,我们只实现其中3个就够了

  1. static xmlSAXHandler _saxHandlerStruct = {  
  2.     NULL,             
  3.     NULL,            
  4.     NULL,             
  5.     NULL,             
  6.     NULL,             
  7.     NULL,             
  8.     NULL,             
  9.     NULL,             
  10.     NULL,             
  11.     NULL,             
  12.     NULL,             
  13.     NULL,             
  14.     NULL,             
  15.     NULL,             
  16.     NULL,             
  17.     NULL,             
  18.     NULL,             
  19.     charactersFoundHandler,  
  20.     NULL,             
  21.     NULL,             
  22.     NULL,             
  23.     NULL,             
  24.     NULL,             
  25.     NULL,             
  26.     NULL,             
  27.     NULL,             
  28.     NULL,             
  29.     XML_SAX2_MAGIC,   
  30.     NULL,             
  31.     startElementHandler,     
  32.     endElementHandler,       
  33.     NULL,             
  34. }; 

机构体中填入了我们准备实现的3个方法句柄,因此我们还应当定义这3个方法。由于结构体是静态的,只能访问静态成员,所以这3个方法也是静态的:

  1. //3个静态方法的实现,其实是调用了参数ctx的成员方法, ctx在_parserContext初始化时传入  
  2. static void startElementHandler(  
  3.                                 void* ctx,  
  4.                                 const xmlChar* localname,  
  5.                                 const xmlChar* prefix,  
  6.                                 const xmlChar* URI,  
  7.                                 int nb_namespaces,  
  8.                                 const xmlChar** namespaces,  
  9.                                 int nb_attributes,  
  10.                                 int nb_defaulted,  
  11.                                 const xmlChar** attributes)  
  12. {  
  13.     [(BaseXmlParser*)ctx  
  14.      startElementLocalName:localname  
  15.      prefix:prefix URI:URI  
  16.      nb_namespaces:nb_namespaces  
  17.      namespaces:namespaces  
  18.      nb_attributes:nb_attributes  
  19.      nb_defaulted:nb_defaulted  
  20.      attributes:attributes];  
  21. }  
  22. static void endElementHandler(  
  23.                               void* ctx,  
  24.                               const xmlChar* localname,  
  25.                               const xmlChar* prefix,  
  26.                              const xmlChar* URI)  
  27.  
  28. {  
  29.     [(BaseXmlParser*)ctx  
  30.      endElementLocalName:localname  
  31.      prefix:prefix  
  32.      URI:URI];  
  33. }  
  34. static void charactersFoundHandler(  
  35.                                    void* ctx,  
  36.                                    const xmlChar* ch,  
  37.                                    int len)  
  38. {  
  39.     [(BaseXmlParser*)ctx  
  40.      charactersFound:ch len:len];  

其实这3个静态方法只是调用了超类BaseXmlParser的成员方法,他的具体类型依赖于ctx的注入类型,也就是说,这里的ctx可以是任何BaseXmlParser的子类。 实际使用中,我们应该注入其子类,从而可以根据不同的情况为URLOperation“注入”不同的解析器,实现解析不同的xml文件的目的。

现在,需要把解析器应用到NSURLConnection的委托方法中(这里省略了部分代码,只列出了新增加的部分):

  1. #pragma mark NSURLConnection delegate Method  
  2. // 接收到数据(增量)时  
  3. - (void)connection:(NSURLConnection*)connection  
  4.     didReceiveData:(NSData*)data {  
  5.     // 使用libxml解析器进行xml解析  
  6.     xmlParseChunk(_parserContext, (const char*)[data bytes], [data length], 0);  
  7.          ⋯⋯  
  8. }  
  9. // HTTP请求结束时  
  10. - (void)connectionDidFinishLoading:(NSURLConnection*)connection {  
  11.              if(baseParser!=nil && baseParser!=NULL){  
  12.         [self setData:[[NSDictionary alloc] initWithDictionary:[baseParser getResult]]];  
  13.     }else {  
  14.         NSLog(@"baseparser is nil");  
  15.     }  
  16.     // 添加解析数据(结束),注意最后一个参数termindate  
  17.     xmlParseChunk(_parserContext, NULL, 0, 1);  
  18.     // 释放XML解析器  
  19.     if (_parserContext) {  
  20.         xmlFreeParserCtxt(_parserContext), _parserContext = NULL;  
  21.     }  
  22. ⋯⋯  
  23. }  
  24.  
  25. -(void)connection: (NSURLConnection *) connection didFailWithError: (NSError *) error{  
  26.     // 释放XML解析器  
  27.     if (_parserContext) {  
  28.         xmlFreeParserCtxt(_parserContext), _parserContext = NULL;  
  29.     }  
  30.          ⋯⋯  
  31. }  
  32. @end 

接下来,在“登录”按钮中代码也要做相应的修改,因为URLOperation的构造函数要求传递一个具体的xml解析器对象:

  1. //构造xmlparser  
  2. DLTLoginParser* parser=[[DLTLoginParser alloc]init];  
  3. URLOperation* operation=[[URLOperation alloc ]initWithURLString:url xmlParser:parser];  
  4. [parser release]; 

然后,在接收变更通知方法中打印解析结果:

  1. URLOperation* ctx=(URLOperation*)context;  
  2. NSLog(@"%@",[ctx data]); 

后台打印结果:

  1. {  
  2.     items =     (  
  3.                 {  
  4.             name = "\U4e91\U7535\U4f01\U4fe1\U901a";  
  5.         },  
  6.                 {  
  7.             name = "\U79fb\U52a8\U8c03\U5ea6";  
  8.         },  
  9.                 {  
  10.             name = "\U79fb\U52a8\U62a2\U4fee";  
  11.         }  
  12.     );  
  13.     "login_status" = true;  

小结:深度解析Cocoa异步请求libxml2.dylib教程的内容介绍完了,希望本文对你有所帮助!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值