通過大眾點評平台開發來簡單了解一下,oc的網絡編程和數據解析(json)
首先我們需要到大大眾點評開發者平台申請一個key。http://developer.dianping.com/app/tech/api這個網站有api文檔。
本文沒有使用第三方庫,網絡其請求使用NSURLConnection類,json數據解析使用NSJSONSerialization。這個例子是根據條件獲取商戶信息。
1.定義常量
#define kSearchResto @"v1/business/find_businesses" #define kDPDomain @"http://api.dianping.com/" #define kDPAppKey @"。。。" #define kDPAppSecret @"。。。" #define kWFRequestimeOutInterval 60.0 #define kHttpMethodGET @"GET" #define kHttpMethodPOST @"POST"
2.創建數據類,包含商戶信息
// // WFBusiness.h // WFSearch // // Created by ForrestWoo on 14-9-5. // Copyright (c) 2014年 ForrestWoo. All rights reserved. // #import <Foundation/Foundation.h> #import "WFRootModel.h" @interface WFBusiness : WFRootModel @property (nonatomic, assign) int business_id; @property (nonatomic, strong) NSString *name; @property (nonatomic, strong) NSString *branch_name; @property (nonatomic, strong) NSString *address; @property (nonatomic, strong) NSString *telephone; @property (nonatomic, strong) NSString *city; //星級圖片鏈接。 @property (nonatomic, strong) NSString *rating_img_url; @property (nonatomic, strong) NSString *rating_s_img_url; @property (nonatomic, strong) NSArray *regions; @property (nonatomic, strong) NSArray *categories; //經緯度信息 @property (nonatomic, assign) float latitude; @property (nonatomic, assign) float longitude; @property (nonatomic, assign) float avg_rating; @property (nonatomic, assign) float product_score; @property (nonatomic, assign) float decoration_score; @property (nonatomic, assign) float service_score; //評價 分五種。1:一般,2:尚可,3:好,4:很好,5:非常好 @property (nonatomic, assign) int product_grade; @property (nonatomic, assign) int decoration_grade; @property (nonatomic, assign) int service_grade; //人均消費。 @property (nonatomic, assign) int avg_price; //點評。 @property (nonatomic, assign) int review_count; @property (nonatomic, strong) NSArray *review_list_url; @property (nonatomic, assign) int distance; @property (nonatomic, strong) NSString *business_url; //圖片 @property (nonatomic, strong) NSString *photo_url; @property (nonatomic, strong) NSString *s_photo_url; @property (nonatomic, assign) int photo_count; @property (nonatomic, strong) NSArray *photo_list_url; //優惠券。 @property (nonatomic, assign) int has_coupon; @property (nonatomic, assign) int coupon_id; @property (nonatomic, strong) NSString *coupon_description; @property (nonatomic, strong) NSString *coupon_url; //團購信息。 @property (nonatomic, assign) int has_deal; @property (nonatomic, assign) int deal_count; @property (nonatomic, strong) NSArray *deals; @property (nonatomic, strong) NSString *deals_id; @property (nonatomic, strong) NSString *deals_description; @property (nonatomic, strong) NSString *deals_url; @property (nonatomic, assign) int has_online_reservation; @property (nonatomic, strong) NSString *online_reservation_url; - (id)initWithJson:(NSDictionary *)jsonData; @end
這里的屬性名字並不是隨便定義的,而是根據服務器端返回的字段名稱定義的,它與返回的名稱是一致的,目的是方便以后通過反射對類的屬性賦值,這樣我們不需要一一賦值,詳情請繼續
3.WFRootModel
#import <Foundation/Foundation.h> //根類,定義一些通用的屬性和消息 @interface WFRootModel : NSObject //獲取類的屬性名稱,以通過反射對對象的屬性賦值。 - (NSArray *)PropertyKeys; @end #import "WFRootModel.h" #import <objc/runtime.h> @implementation WFRootModel - (NSArray *)PropertyKeys { unsigned int outCount,i; objc_property_t *pp = class_copyPropertyList([self class], &outCount); NSMutableArray *keys = [[NSMutableArray alloc] initWithCapacity:0]; for (i = 0; i < outCount; i++) { objc_property_t property = pp[i]; NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; [keys addObject:propertyName]; } return keys; } @end
此類賦予了它的子類一個重要行為,它只包含一個消息[- (NSArray *)PropertyKeys],它的作用是子類能夠獲取自己包含的所有屬性。反射中會使用這些屬性。
4.創建請求url
大眾點評請求使用SHA加密。
[1]SHA加密類SHA1,需要引入
CommonDigest.h
@interface SHA1 : NSObject + (NSString *)getSHAString:(NSData *)aStringbytes; @end #import <CommonCrypto/CommonDigest.h> #import "SHA1.h" @implementation SHA1 + (NSString *)getSHAString:(NSData *)aStringbytes { unsigned char digest[CC_SHA1_DIGEST_LENGTH]; if (CC_SHA1([aStringbytes bytes], [aStringbytes length], digest)) { NSMutableString *digestString = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH]; for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) { unsigned char aChar = digest[i]; [digestString appendFormat:@"%02x",aChar]; } return digestString; } else { return nil; } } @end
由於我對加密了解很少,所以就不做介紹了,只給出一個方法,供大家參考。
[2]返回根url,因為根url會常用到,我們就將它封裝起來,以便后用
- (NSString *)getRootURL { return kDPDomain; }
[3]返回合法的url
- (NSString *)serializeURL:(NSString *)aBaseURL params:(NSDictionary *)aParams { NSURL* parsedURL = [NSURL URLWithString:[aBaseURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; NSMutableDictionary *paramsDic = [NSMutableDictionary dictionaryWithDictionary:[self parseQueryString:[parsedURL query]]]; if (aParams) { [paramsDic setValuesForKeysWithDictionary:aParams]; } NSMutableString *signString = [NSMutableString stringWithString:kDPAppKey]; NSMutableString *paramsString = [NSMutableString stringWithFormat:@"appkey=%@", kDPAppKey]; NSArray *sortedKeys = [[paramsDic allKeys] sortedArrayUsingSelector: @selector(compare:)]; for (NSString *key in sortedKeys) { [signString appendFormat:@"%@%@", key, [paramsDic objectForKey:key]]; [paramsString appendFormat:@"&%@=%@", key, [paramsDic objectForKey:key]]; } NSString *str = kDPAppSecret; [signString appendString:str]; NSData *stringBytes = [signString dataUsingEncoding: NSUTF8StringEncoding]; NSString *digestString = [SHA1 getSHAString:stringBytes]; if (digestString) { [paramsString appendFormat:@"&sign=%@", [digestString uppercaseString]]; NSLog(@"...%@...", [NSString stringWithFormat:@"%@://%@%@?%@", [parsedURL scheme], [parsedURL host], [parsedURL path], [paramsString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]); return [NSString stringWithFormat:@"%@://%@%@?%@", [parsedURL scheme], [parsedURL host], [parsedURL path], [paramsString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; } else { return nil; } }
5.創建網絡連接,發送請求
使用NSURLConnection來完成網絡連接
- (void)createConnectionWithUrl:(NSString *)urlString params:(NSDictionary *)aParams delegate:(id<WFRequestDelegate>)aDelegate { NSString *urlStr = [self serializeURL:[self generateFullURL:urlString] params:aParams]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:kWFRequestimeOutInterval]; [request setHTTPMethod:kHttpMethodGET]; NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; self.delegate = aDelegate; // [[[self class] sharedInstance] setConnections:conn]; }
6.返回結果以及數據解析
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { _responseData = [[NSMutableData alloc] init]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [_responseData appendData:data]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSError *error = nil; id result= [NSJSONSerialization JSONObjectWithData:_responseData options:NSJSONReadingAllowFragments error:&error]; if (!result) { NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:error,@"error", nil]; NSError *err = [NSError errorWithDomain:@"domain is error" code:-1 userInfo:userInfo]; if ([self.delegate respondsToSelector:@selector(request:didfailWithError:)]) { [self.delegate request:self didfailWithError:err]; } } else { NSString *status = 0; if ([result isKindOfClass:[NSDictionary class]]) { status = [result objectForKey:@"status"]; } if ([status isEqualToString:@"OK"]) { if ([self.delegate respondsToSelector:@selector(request:didfinishloadingWithResult:)]) { [self.delegate request:self didfinishloadingWithResult:result == nil ? _responseData : result]; } } else { if ([status isEqualToString:@"ERROR"]) { //TODO:錯誤處理代碼。 } } } // NSString *str = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding]; // NSLog(@"_responseData:%@", str); // [self clearData]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"ERROR!%@", error.description); }
返回的數據存儲在一個WFFindBusinessesResult的實例中。
//super class for result. @interface WFUrlResult : NSObject @property (nonatomic, strong) NSString *status; @property (nonatomic, assign) NSInteger total_count; @property (nonatomic, assign) NSInteger count; - (id)initWithJson:(NSDictionary *)jsonData; @end @interface WFFindBusinessesResult : WFUrlResult @property (nonatomic, strong) NSArray *businesses; @end
對對象賦值非常簡單
@implementation WFFindBusinessesResult - (id)initWithJson:(NSDictionary *)jsonData { self.status = [jsonData objectForKey:@"status"]; self.count = [[jsonData objectForKey:@"count"] integerValue]; self.total_count = [[jsonData objectForKey:@"total_count"] integerValue]; NSArray *arr = nil; NSMutableArray *businessArr = [[NSMutableArray alloc] initWithCapacity:0]; if ([jsonData isKindOfClass:[NSDictionary class]]) { arr = [jsonData objectForKey:@"businesses"];
//賦值,通過反射賦值,根本不需要一一對字段賦值。 for (id obj in arr) { WFBusiness *bus = [[WFBusiness alloc] init]; NSArray *propertyList = [[[WFBusiness alloc] init] PropertyKeys]; for (NSString *key in propertyList) { [bus setValue:[obj objectForKey:key] forKey:key]; } [businessArr addObject:bus]; NSLog(@"photo is %@", [bus categories]); } } NSLog(@"businessArr count is %lu", [businessArr count]); self.Businesses = businessArr; return self; } @end
完整的封裝類WFHTTPRequest。它用於創建一個連接,並發送請求,管理整個應用出現的網絡連接,創建一個單例,全局唯一。
// // WFRequest.h // WFSearch // // Created by ForrestWoo on 14-8-30. // Copyright (c) 2014年 ForrestWoo. All rights reserved. // #import <Foundation/Foundation.h> @class WFHTTPRequest; @protocol WFRequestDelegate <NSObject> @optional - (void)request:(WFHTTPRequest *)request didfinishloadingWithResult:(id)aResult; - (void)request:(WFHTTPRequest *)request didfailWithError:(NSError *)aError; @end @interface WFHTTPRequest : NSObject @property (nonatomic, unsafe_unretained) id<WFRequestDelegate> delegate; @property (nonatomic, strong) NSString *aUrl; @property (nonatomic, strong) NSDictionary *aParams; @property (nonatomic, strong) NSArray *connections; + (WFHTTPRequest *)sharedInstance; - (void)createConnectionWithUrl:(NSString *)urlString params:(NSDictionary *)aParams delegate:(id<WFRequestDelegate>)aDelegate; - (void) cancel; - (NSString *)getRootURL; //api - (void)findBusinessesWithParams:(NSDictionary *)aParams delegate:(id<WFRequestDelegate>)aDelegate; - (void)findCityWithDelegate:(id<WFRequestDelegate>)aDelegate; - (void)getCategoriesForBusinessesWithDelegate:(id<WFRequestDelegate>)aDelegate; @end
// // WFRequest.m // WFSearch // // Created by ForrestWoo on 14-8-30. // Copyright (c) 2014年 ForrestWoo. All rights reserved. // #import "WFHTTPRequest.h" #import "WFConstants.h" #import "SHA1.h" @interface WFHTTPRequest () <NSURLConnectionDataDelegate> //- (void)appendUTF8Body:(NSMutableData *)aBody dataString:(NSString *)aDataString; //- (NSDictionary *)parseQueryString:(NSString *)query; ////- (void)handleResponseData:(NSData *)data; //- (NSString *)generateFullURL:(NSString *)url; ////- (void)addConnection; //- (NSString *)serializeURL:(NSString *)aBaseURL params:(NSDictionary *)aParams; @end static WFHTTPRequest *sharedObj = nil; @implementation WFHTTPRequest { NSURLConnection *_connection; NSMutableData *_responseData; } - (NSString *)getRootURL { return kDPDomain; } - (NSString *)generateFullURL:(NSString *)url { NSMutableString *str = [[NSMutableString alloc] initWithCapacity:0]; [str appendString:[self getRootURL]]; [str appendString:url]; return str; } + (WFHTTPRequest *)sharedInstance { static dispatch_once_t pred = 0; dispatch_once(&pred, ^{ sharedObj = [[super allocWithZone:NULL] init]; }); return sharedObj; } + (id)allocWithZone:(struct _NSZone *)zone { return [self sharedInstance]; } - (void)createConnectionWithUrl:(NSString *)urlString params:(NSDictionary *)aParams delegate:(id<WFRequestDelegate>)aDelegate { NSString *urlStr = [self serializeURL:[self generateFullURL:urlString] params:aParams]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:kWFRequestimeOutInterval]; [request setHTTPMethod:kHttpMethodGET]; NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; self.delegate = aDelegate; // [[[self class] sharedInstance] setConnections:conn]; } - (void)findBusinessesWithParams:(NSDictionary *)aParams delegate:(id<WFRequestDelegate>)aDelegate { [self createConnectionWithUrl:kSearchResto params:aParams delegate:aDelegate]; } - (void)findCityWithDelegate:(id<WFRequestDelegate>)aDelegate { [self createConnectionWithUrl:kSearchCity params:nil delegate:aDelegate]; } - (void)getCategoriesForBusinessesWithDelegate:(id<WFRequestDelegate>)aDelegate { [self createConnectionWithUrl:kCategoriesWithBusinesses params:nil delegate:aDelegate]; } //- (void)appendUTF8Body:(NSMutableData *)aBody dataString:(NSString *)aDataString //{ // [aBody appendData:[aDataString dataUsingEncoding:NSUTF8StringEncoding]]; //} - (NSDictionary *)parseQueryString:(NSString *)query { NSMutableDictionary *paramDict = [[NSMutableDictionary alloc] initWithDictionary:0]; NSArray *paramArr = [query componentsSeparatedByString:@"&"]; for (NSString *param in paramArr) { NSArray * elements = [param componentsSeparatedByString:@"="]; if ([elements count] <= 1) { return nil; } NSString *key = [[elements objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSString *value = [[elements objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; [paramDict setObject:value forKey:key]; } return paramDict; } - (NSString *)serializeURL:(NSString *)aBaseURL params:(NSDictionary *)aParams { NSURL* parsedURL = [NSURL URLWithString:[aBaseURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; NSMutableDictionary *paramsDic = [NSMutableDictionary dictionaryWithDictionary:[self parseQueryString:[parsedURL query]]]; if (aParams) { [paramsDic setValuesForKeysWithDictionary:aParams]; } NSMutableString *signString = [NSMutableString stringWithString:kDPAppKey]; NSMutableString *paramsString = [NSMutableString stringWithFormat:@"appkey=%@", kDPAppKey]; NSArray *sortedKeys = [[paramsDic allKeys] sortedArrayUsingSelector: @selector(compare:)]; for (NSString *key in sortedKeys) { [signString appendFormat:@"%@%@", key, [paramsDic objectForKey:key]]; [paramsString appendFormat:@"&%@=%@", key, [paramsDic objectForKey:key]]; } NSString *str = kDPAppSecret; [signString appendString:str]; NSData *stringBytes = [signString dataUsingEncoding: NSUTF8StringEncoding]; NSString *digestString = [SHA1 getSHAString:stringBytes]; if (digestString) { [paramsString appendFormat:@"&sign=%@", [digestString uppercaseString]]; NSLog(@"...%@...", [NSString stringWithFormat:@"%@://%@%@?%@", [parsedURL scheme], [parsedURL host], [parsedURL path], [paramsString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]); return [NSString stringWithFormat:@"%@://%@%@?%@", [parsedURL scheme], [parsedURL host], [parsedURL path], [paramsString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; } else { return nil; } } - (void) cancel { [_connection cancel]; } + (NSString *)getParamValueFromURL:(NSString *)aURl paramName:(NSString *)aParamName { return nil; } #pragma mark - NSURLConnection Delegate Methods - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { _responseData = [[NSMutableData alloc] init]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [_responseData appendData:data]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSError *error = nil; id result= [NSJSONSerialization JSONObjectWithData:_responseData options:NSJSONReadingAllowFragments error:&error]; if (!result) { NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:error,@"error", nil]; NSError *err = [NSError errorWithDomain:@"domain is error" code:-1 userInfo:userInfo]; if ([self.delegate respondsToSelector:@selector(request:didfailWithError:)]) { [self.delegate request:self didfailWithError:err]; } } else { NSString *status = 0; if ([result isKindOfClass:[NSDictionary class]]) { status = [result objectForKey:@"status"]; } if ([status isEqualToString:@"OK"]) { if ([self.delegate respondsToSelector:@selector(request:didfinishloadingWithResult:)]) { [self.delegate request:self didfinishloadingWithResult:result == nil ? _responseData : result]; } } else { if ([status isEqualToString:@"ERROR"]) { //TODO:錯誤處理代碼。 } } } // NSString *str = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding]; // NSLog(@"_responseData:%@", str); // [self clearData]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"ERROR!%@", error.description); } - (void)clearData { _responseData = nil; [_connection cancel]; _connection = nil; } @end
代碼下載。下節詳細介紹數據解析和網絡編程,反射(運行時)