From 206f6ff1f62dde0bf4c57b89a95cb5d790293f43 Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Tue, 8 Nov 2011 11:12:44 -0600 Subject: [PATCH] Switching to NSInvocation approach to dynamic JSON library-agnostic encoding and decoding, appropriating @steipete's work on HockeyApp --- AFNetworking/AFJSONUtilities.h | 172 +++++++++++++++-------- Mac Example/Classes/AFGowallaAPIClient.h | 2 - iOS Example/Classes/AFGowallaAPIClient.h | 2 - 3 files changed, 113 insertions(+), 63 deletions(-) diff --git a/AFNetworking/AFJSONUtilities.h b/AFNetworking/AFJSONUtilities.h index 0ffb519..0f73ea6 100644 --- a/AFNetworking/AFJSONUtilities.h +++ b/AFNetworking/AFJSONUtilities.h @@ -24,69 +24,123 @@ #include -#if defined(_AF_USE_JSONKIT) -#import "JSONKit.h" -#elif defined(_AF_USE_SBJSON) -#import "SBJSON.h" - -static SBJsonParser * _SBJSONParser() { - static SBJsonParser *_af_SBJSONParser = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _af_SBJSONParser = [[SBJsonParser alloc] init]; - }); - - return _af_SBJSONParser; -} - -static SBJsonWriter * _SBJSONWriter() { - static SBJsonWriter *_af_SBJSONWriter = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _af_SBJSONWriter = [[SBJsonWriter alloc] init]; - }); - - return _af_SBJSONWriter; -} -#elif defined(_AF_USE_YAJL) - #if __IPHONE_OS_VERSION_MIN_REQUIRED - #import - #elif __MAC_OS_X_VERSION_MIN_REQUIRED - #import - #endif -#endif - static inline NSData * AFJSONEncode(id object, NSError **error) { -#if defined(_AF_USE_JSONKIT) - return [object JSONData]; -#elif defined(_AF_USE_SBJSON) - SBJsonWriter *writer = _SBJSONWriter(); - return [writer dataWithObject:object]; -#elif defined(_AF_USE_YAJL) - return [[object yajl_JSONString] dataUsingEncoding:NSUTF8StringEncoding]]; -#else - if ([NSJSONSerialization class]) { - return [NSJSONSerialization dataWithJSONObject:object options:0 error:error]; - } -#endif + NSData *data = nil; - return nil; + id _NSJSONSerializationClass = NSClassFromString(@"NSJSONSerialization"); + SEL _NSJSONSerializationSelector = NSSelectorFromString(@"dataWithJSONObject:options:error:"); + SEL _JSONKitSelector = NSSelectorFromString(@"JSONDataWithOptions:error:"); + SEL _SBJSONSelector = NSSelectorFromString(@"JSONRepresentation"); + SEL _YAJLSelector = NSSelectorFromString(@"yajl_JSONString"); + + if (_NSJSONSerializationClass && [_NSJSONSerializationClass respondsToSelector:_NSJSONSerializationSelector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[_NSJSONSerializationClass methodSignatureForSelector:_NSJSONSerializationSelector]]; + invocation.target = _NSJSONSerializationClass; + invocation.selector = _NSJSONSerializationSelector; + + [invocation setArgument:&object atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation + NSUInteger writeOptions = 0; + [invocation setArgument:&writeOptions atIndex:3]; + [invocation setArgument:error atIndex:4]; + + [invocation invoke]; + [invocation getReturnValue:&data]; + } else if (_JSONKitSelector && [data respondsToSelector:_JSONKitSelector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[data methodSignatureForSelector:_JSONKitSelector]]; + invocation.target = data; + invocation.selector = _JSONKitSelector; + + NSUInteger serializeOptionFlags = 0; + [invocation setArgument:&serializeOptionFlags atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation + [invocation setArgument:error atIndex:3]; + + [invocation invoke]; + [invocation getReturnValue:&data]; + } else if (_SBJSONSelector && [data respondsToSelector:_SBJSONSelector]) { + NSString *JSONString = nil; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[data methodSignatureForSelector:_SBJSONSelector]]; + invocation.target = data; + invocation.selector = _SBJSONSelector; + + [invocation invoke]; + [invocation getReturnValue:&data]; + + data = [JSONString dataUsingEncoding:NSUTF8StringEncoding]; + } else if (_YAJLSelector && [data respondsToSelector:_YAJLSelector]) { + @try { + NSString *JSONString = nil; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[data methodSignatureForSelector:_YAJLSelector]]; + invocation.target = data; + invocation.selector = _YAJLSelector; + + [invocation invoke]; + [invocation getReturnValue:&JSONString]; + + data = [JSONString dataUsingEncoding:NSUTF8StringEncoding]; + } + @catch (NSException *exception) { + *error = [[[NSError alloc] initWithDomain:NSStringFromClass([exception class]) code:0 userInfo:[exception userInfo]] autorelease]; + } + } + + return data; } -static inline id AFJSONDecode(NSData *data, NSError **error) { +static inline id AFJSONDecode(NSData *data, NSError **error) { + id JSON = nil; -#if defined(_AF_USE_JSONKIT) - return [[JSONDecoder decoder] objectWithData:data error:error]; -#elif defined(_AF_USE_SBJSON) - SBJsonParser *parser = _SBJsonParser(); - return [parser objectWithData:data]; -#elif defined(_AF_USE_YAJL) - return [data yajl_JSON]; -#else - if ([NSJSONSerialization class]) { - return [NSJSONSerialization JSONObjectWithData:data options:0 error:error]; - } -#endif + id _NSJSONSerializationClass = NSClassFromString(@"NSJSONSerialization"); + SEL _NSJSONSerializationSelector = NSSelectorFromString(@"JSONObjectWithData:options:error:"); + SEL _JSONKitSelector = NSSelectorFromString(@"objectFromJSONDataWithParseOptions:error:"); + SEL _SBJSONSelector = NSSelectorFromString(@"JSONValue"); + SEL _YAJLSelector = NSSelectorFromString(@"yajl_JSONWithOptions:error:"); + + if (_NSJSONSerializationClass && [_NSJSONSerializationClass respondsToSelector:_NSJSONSerializationSelector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[_NSJSONSerializationClass methodSignatureForSelector:_NSJSONSerializationSelector]]; + invocation.target = _NSJSONSerializationClass; + invocation.selector = _NSJSONSerializationSelector; - return nil; + [invocation setArgument:&data atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation + NSUInteger readOptions = 0; + [invocation setArgument:&readOptions atIndex:3]; + [invocation setArgument:error atIndex:4]; + + [invocation invoke]; + [invocation getReturnValue:&JSON]; + } else if (_JSONKitSelector && [data respondsToSelector:_JSONKitSelector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[data methodSignatureForSelector:_JSONKitSelector]]; + invocation.target = data; + invocation.selector = _JSONKitSelector; + + NSUInteger parseOptionFlags = 0; + [invocation setArgument:&parseOptionFlags atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation + [invocation setArgument:error atIndex:3]; + + [invocation invoke]; + [invocation getReturnValue:&JSON]; + } else if (_SBJSONSelector && [data respondsToSelector:_SBJSONSelector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[data methodSignatureForSelector:_SBJSONSelector]]; + invocation.target = data; + invocation.selector = _SBJSONSelector; + + [invocation invoke]; + [invocation getReturnValue:&JSON]; + } else if (_YAJLSelector && [data respondsToSelector:_YAJLSelector]) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[data methodSignatureForSelector:_YAJLSelector]]; + invocation.target = data; + invocation.selector = _YAJLSelector; + + NSUInteger yajlParserOptions = 0; + [invocation setArgument:&yajlParserOptions atIndex:2]; // arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation + [invocation setArgument:error atIndex:3]; + + [invocation invoke]; + [invocation getReturnValue:&JSON]; + } + + return JSON; } + + + + diff --git a/Mac Example/Classes/AFGowallaAPIClient.h b/Mac Example/Classes/AFGowallaAPIClient.h index e4095e4..5fabb8c 100644 --- a/Mac Example/Classes/AFGowallaAPIClient.h +++ b/Mac Example/Classes/AFGowallaAPIClient.h @@ -23,8 +23,6 @@ #import #import "AFHTTPClient.h" -#define _AF_USE_JSONKIT - extern NSString * const kAFGowallaClientID; extern NSString * const kAFGowallaBaseURLString; diff --git a/iOS Example/Classes/AFGowallaAPIClient.h b/iOS Example/Classes/AFGowallaAPIClient.h index e4095e4..5fabb8c 100644 --- a/iOS Example/Classes/AFGowallaAPIClient.h +++ b/iOS Example/Classes/AFGowallaAPIClient.h @@ -23,8 +23,6 @@ #import #import "AFHTTPClient.h" -#define _AF_USE_JSONKIT - extern NSString * const kAFGowallaClientID; extern NSString * const kAFGowallaBaseURLString;