ResponseDeserializer for separating deserialization and service logic

This commit is contained in:
Mateusz Mackowiak 2016-05-06 10:46:03 +02:00
parent 1dd8faf8b7
commit 65d85b7760
13 changed files with 627 additions and 364 deletions

View File

@ -223,6 +223,8 @@ public class ObjcClientCodegen extends DefaultCodegen implements CodegenConfig {
supportingFiles.add(new SupportingFile("JSONResponseSerializer-body.mustache", swaggerFolder, classPrefix + "JSONResponseSerializer.m"));
supportingFiles.add(new SupportingFile("JSONRequestSerializer-body.mustache", swaggerFolder, classPrefix + "JSONRequestSerializer.m"));
supportingFiles.add(new SupportingFile("JSONRequestSerializer-header.mustache", swaggerFolder, classPrefix + "JSONRequestSerializer.h"));
supportingFiles.add(new SupportingFile("ResponseDeserializer-body.mustache", swaggerFolder, classPrefix + "ResponseDeserializer.m"));
supportingFiles.add(new SupportingFile("ResponseDeserializer-header.mustache", swaggerFolder, classPrefix + "ResponseDeserializer.h"));
supportingFiles.add(new SupportingFile("JSONValueTransformer+ISO8601.m", swaggerFolder, "JSONValueTransformer+ISO8601.m"));
supportingFiles.add(new SupportingFile("JSONValueTransformer+ISO8601.h", swaggerFolder, "JSONValueTransformer+ISO8601.h"));
supportingFiles.add(new SupportingFile("Configuration-body.mustache", swaggerFolder, classPrefix + "Configuration.m"));

View File

@ -2,10 +2,6 @@
NSString *const {{classPrefix}}ResponseObjectErrorKey = @"{{classPrefix}}ResponseObject";
NSString *const {{classPrefix}}DeserializationErrorDomainKey = @"{{classPrefix}}DeserializationErrorDomainKey";
NSInteger const {{classPrefix}}TypeMismatchErrorCode = 143553;
static long requestId = 0;
static bool offlineState = false;
static NSMutableSet * queuedRequests = nil;
@ -33,6 +29,7 @@ static void (^reachabilityChangeBlock)(int);
self.requestSerializer = [AFJSONRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.securityPolicy = [self customSecurityPolicy];
self.responseDeserializer = [[{{classPrefix}}ResponseDeserializer alloc] init];
// configure reachability
[self configureCacheReachibility];
}
@ -290,154 +287,6 @@ static void (^reachabilityChangeBlock)(int);
[self.reachabilityManager startMonitoring];
}
#pragma mark - Deserialize methods
- (id) deserialize:(id) data class:(NSString *) class error:(NSError **) error {
// return nil if data is nil or class is nil
if (!data || !class) {
return nil;
}
// remove "*" from class, if ends with "*"
if ([class hasSuffix:@"*"]) {
class = [class substringToIndex:[class length] - 1];
}
// pure object
if ([class isEqualToString:@"NSObject"]) {
return data;
}
NSRegularExpression *regexp = nil;
NSTextCheckingResult *match = nil;
NSMutableArray *resultArray = nil;
NSMutableDictionary *resultDict = nil;
NSString *innerType = nil;
// list of models
NSString *arrayOfModelsPat = @"NSArray<(.+)>";
regexp = [NSRegularExpression regularExpressionWithPattern:arrayOfModelsPat
options:NSRegularExpressionCaseInsensitive
error:nil];
match = [regexp firstMatchInString:class
options:0
range:NSMakeRange(0, [class length])];
if (match) {
if(![data isKindOfClass: [NSArray class]]) {
if(error) {
NSDictionary * userInfo = @{NSLocalizedDescriptionKey : NSLocalizedString(@"Received response is not an array", nil)};
*error = [NSError errorWithDomain:{{classPrefix}}DeserializationErrorDomainKey code:{{classPrefix}}TypeMismatchErrorCode userInfo:userInfo];
}
return nil;
}
NSArray *dataArray = data;
innerType = [class substringWithRange:[match rangeAtIndex:1]];
resultArray = [NSMutableArray arrayWithCapacity:[dataArray count]];
[data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id arrObj = [self deserialize:obj class:innerType error:error];
if(arrObj) {
[resultArray addObject:arrObj];
} else {
* stop = YES;
}
}];
return resultArray;
}
// list of primitives
NSString *arrayOfPrimitivesPat = @"NSArray\\* /\\* (.+) \\*/";
regexp = [NSRegularExpression regularExpressionWithPattern:arrayOfPrimitivesPat
options:NSRegularExpressionCaseInsensitive
error:nil];
match = [regexp firstMatchInString:class
options:0
range:NSMakeRange(0, [class length])];
if (match) {
NSArray *dataArray = data;
innerType = [class substringWithRange:[match rangeAtIndex:1]];
resultArray = [NSMutableArray arrayWithCapacity:[dataArray count]];
[data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id arrObj = [self deserialize:obj class:innerType error:error];
if(arrObj) {
[resultArray addObject:arrObj];
} else {
* stop = YES;
}
}];
return resultArray;
}
// map
NSString *dictPat = @"NSDictionary\\* /\\* (.+?), (.+) \\*/";
regexp = [NSRegularExpression regularExpressionWithPattern:dictPat
options:NSRegularExpressionCaseInsensitive
error:nil];
match = [regexp firstMatchInString:class
options:0
range:NSMakeRange(0, [class length])];
if (match) {
NSDictionary *dataDict = data;
NSString *valueType = [class substringWithRange:[match rangeAtIndex:2]];
resultDict = [NSMutableDictionary dictionaryWithCapacity:[dataDict count]];
[data enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
id dicObj = [self deserialize:obj class:valueType error:error];
if(dicObj) {
[resultDict setValue:dicObj forKey:key];
} else {
* stop = YES;
}
}];
return resultDict;
}
// primitives
NSArray *primitiveTypes = @[@"NSString", @"NSDate", @"NSNumber"];
if ([primitiveTypes containsObject:class]) {
if ([class isEqualToString:@"NSString"]) {
return [NSString stringWithString:data];
}
else if ([class isEqualToString:@"NSDate"]) {
return [NSDate dateWithISO8601String:data];
}
else if ([class isEqualToString:@"NSNumber"]) {
// NSNumber from NSNumber
if ([data isKindOfClass:[NSNumber class]]) {
return data;
}
else if ([data isKindOfClass:[NSString class]]) {
// NSNumber (NSCFBoolean) from NSString
if ([[data lowercaseString] isEqualToString:@"true"] || [[data lowercaseString] isEqualToString:@"false"]) {
return [NSNumber numberWithBool:[data boolValue]];
// NSNumber from NSString
} else {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
return [formatter numberFromString:data];
}
}
}
}
// model
Class ModelClass = NSClassFromString(class);
if ([ModelClass instancesRespondToSelector:@selector(initWithDictionary:error:)]) {
return [(JSONModel *) [ModelClass alloc] initWithDictionary:data error:error];
}
return nil;
}
#pragma mark - Operation Methods
- (void) operationWithCompletionBlock: (NSURLRequest *)request
@ -661,7 +510,7 @@ static void (^reachabilityChangeBlock)(int);
else {
[self operationWithCompletionBlock:request requestId:requestId completionBlock:^(id data, NSError *error) {
NSError * serializationError;
id response = [self deserialize:data class:responseType error:&serializationError];
id response = [self.responseDeserializer deserialize:data class:responseType error:&serializationError];
if(!response && !error){
error = serializationError;
}

View File

@ -5,7 +5,7 @@
#import "{{classPrefix}}JSONRequestSerializer.h"
#import "{{classPrefix}}QueryParamCollection.h"
#import "{{classPrefix}}Configuration.h"
#import "{{classPrefix}}ResponseDeserializer.h"
/**
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen
@ -25,16 +25,6 @@
*/
extern NSString *const {{classPrefix}}ResponseObjectErrorKey;
/**
* A key for deserialization ErrorDomain
*/
extern NSString *const {{classPrefix}}DeserializationErrorDomainKey;
/**
* Code for deserialization type mismatch error
*/
extern NSInteger const {{classPrefix}}TypeMismatchErrorCode;
/**
* Log debug message macro
*/
@ -49,6 +39,7 @@ extern NSInteger const {{classPrefix}}TypeMismatchErrorCode;
/// In order to ensure the HTTPResponseHeaders are correct, it is recommended to initialize one {{classPrefix}}ApiClient instance per thread.
@property(nonatomic, readonly) NSDictionary* HTTPResponseHeaders;
@property(nonatomic, strong) id<{{classPrefix}}ResponseDeserializer> responseDeserializer;
/**
* Clears Cache
*/
@ -176,15 +167,6 @@ extern NSInteger const {{classPrefix}}TypeMismatchErrorCode;
queryParams:(NSDictionary **)querys
WithAuthSettings:(NSArray *)authSettings;
/**
* Deserializes the given data to Objective-C object.
*
* @param data The data will be deserialized.
* @param class The type of objective-c object.
* @param error The error
*/
- (id) deserialize:(id) data class:(NSString *) class error:(NSError**)error;
/**
* Logs request and response
*

View File

@ -0,0 +1,221 @@
#import "{{classPrefix}}ResponseDeserializer.h"
#import <JSONModel/JSONModel.h>
#import <ISO8601/ISO8601.h>
NSString *const {{classPrefix}}DeserializationErrorDomainKey = @"{{classPrefix}}DeserializationErrorDomainKey";
NSInteger const {{classPrefix}}TypeMismatchErrorCode = 143553;
NSInteger const {{classPrefix}}EmptyValueOccurredErrorCode = 143509;
@interface {{classPrefix}}ResponseDeserializer ()
@property (nonatomic, strong) NSNumberFormatter* numberFormatter;
@property (nonatomic, strong) NSArray *primitiveTypes;
@property (nonatomic, strong) NSArray *basicReturnTypes;
@property (nonatomic, strong) NSRegularExpression* arrayOfModelsPatExpression;
@property (nonatomic, strong) NSRegularExpression* arrayOfPrimitivesPatExpression;
@property (nonatomic, strong) NSRegularExpression* dictPatExpression;
@property (nonatomic, strong) NSRegularExpression* dictModelsPatExpression;
@end
@implementation {{classPrefix}}ResponseDeserializer
- (instancetype)init {
self = [super init];
if (self) {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
_numberFormatter = formatter;
_primitiveTypes = @[@"NSString", @"NSDate", @"NSNumber"];
_basicReturnTypes = @[@"NSObject", @"id"];
_arrayOfModelsPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSArray<(.+)>"
options:NSRegularExpressionCaseInsensitive
error:nil];
_arrayOfPrimitivesPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSArray\\* /\\* (.+) \\*/"
options:NSRegularExpressionCaseInsensitive
error:nil];
_dictPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSDictionary\\* /\\* (.+?), (.+) \\*/"
options:NSRegularExpressionCaseInsensitive
error:nil];
_dictModelsPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSDictionary\\<(.+?), (.+)*\\>"
options:NSRegularExpressionCaseInsensitive
error:nil];
}
return self;
}
#pragma mark - Deserialize methods
- (id) deserialize:(id) data class:(NSString *) className error:(NSError **) error {
// return nil if data is nil or className is nil
if (!data || !className || [data isKindOfClass:[NSNull class]]) {
return nil;
}
// remove "*" from className, if ends with "*"
if ([className hasSuffix:@"*"]) {
className = [className substringToIndex:[className length] - 1];
}
// pure object
if ([self.basicReturnTypes containsObject:className]) {
return data;
}
// primitives
if ([self.primitiveTypes containsObject:className]) {
return [self deserializePrimitiveValue:data class:className error:error];
}
NSTextCheckingResult *match = nil;
// list of models
match = [self.arrayOfModelsPatExpression firstMatchInString:className options:0 range:NSMakeRange(0, [className length])];
if (match) {
NSString *innerType = [className substringWithRange:[match rangeAtIndex:1]];
return [self deserializeArrayValue:data innerType:innerType error:error];
}
// list of primitives
match = [self.arrayOfPrimitivesPatExpression firstMatchInString:className options:0 range:NSMakeRange(0, [className length])];
if (match) {
NSString *innerType = [className substringWithRange:[match rangeAtIndex:1]];
return [self deserializeArrayValue:data innerType:innerType error:error];
}
// map
match = [self.dictPatExpression firstMatchInString:className options:0 range:NSMakeRange(0, [className length])];
if (match) {
NSString *valueType = [className substringWithRange:[match rangeAtIndex:2]];
return [self deserializeDictionaryValue:data valueType:valueType error:error];
}
match = [self.dictModelsPatExpression firstMatchInString:className options:0 range:NSMakeRange(0, [className length])];
if (match) {
NSRange range =[match rangeAtIndex:2];
range = NSMakeRange(range.location, [className length] - range.location -1);
NSString *valueType = [className substringWithRange:range];
return [self deserializeDictionaryValue:data valueType:valueType error:error];
}
// model
Class ModelClass = NSClassFromString(className);
if ([ModelClass instancesRespondToSelector:@selector(initWithDictionary:error:)]) {
return [(JSONModel *) [ModelClass alloc] initWithDictionary:data error:error];
}
return nil;
}
- (id) deserializeDictionaryValue:(id) data valueType:(NSString *) valueType error:(NSError**)error {
if(![data isKindOfClass: [NSDictionary class]]) {
if(error) {
*error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSDictionary class]) data:data];
}
return nil;
}
__block NSMutableDictionary *resultDict = [NSMutableDictionary dictionaryWithCapacity:[data count]];
for (id key in [data allKeys]) {
id obj = [data valueForKey:key];
id dicObj = [self deserialize:obj class:valueType error:error];
if(dicObj) {
[resultDict setValue:dicObj forKey:key];
} else if([obj isKindOfClass:[NSNull class]]) {
if(self.treatNullAsError) {
if (error) {
*error = [self emptyValueOccurredError];
}
resultDict = nil;
break;
}
} else {
resultDict = nil;
break;
}
}
return resultDict;
}
- (id) deserializeArrayValue:(id) data innerType:(NSString *) innerType error:(NSError**)error {
if(![data isKindOfClass: [NSArray class]]) {
if(error) {
*error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSArray class]) data:data];
}
return nil;
}
NSMutableArray* resultArray = [NSMutableArray arrayWithCapacity:[data count]];
for (id obj in data) {
id arrObj = [self deserialize:obj class:innerType error:error];
if(arrObj) {
[resultArray addObject:arrObj];
} else if([obj isKindOfClass:[NSNull class]]) {
if(self.treatNullAsError) {
if (error) {
*error = [self emptyValueOccurredError];
}
resultArray = nil;
break;
}
} else {
resultArray = nil;
break;
}
}
return resultArray;
};
- (id) deserializePrimitiveValue:(id) data class:(NSString *) className error:(NSError**)error {
if ([className isEqualToString:@"NSString"]) {
return [NSString stringWithString:data];
}
else if ([className isEqualToString:@"NSDate"]) {
return [self deserializeDateValue:data error:error];
}
else if ([className isEqualToString:@"NSNumber"]) {
// NSNumber from NSNumber
if ([data isKindOfClass:[NSNumber class]]) {
return data;
}
else if ([data isKindOfClass:[NSString class]]) {
// NSNumber (NSCFBoolean) from NSString
if ([[data lowercaseString] isEqualToString:@"true"] || [[data lowercaseString] isEqualToString:@"false"]) {
return @([data boolValue]);
// NSNumber from NSString
} else {
NSNumber* formattedValue = [self.numberFormatter numberFromString:data];
if(!formattedValue && [data length] > 0 && error) {
*error = [self typeMismatchErrorWithExpectedType:className data:data];
}
return formattedValue;
}
}
}
if(error) {
*error = [self typeMismatchErrorWithExpectedType:className data:data];
}
return nil;
}
- (id) deserializeDateValue:(id) data error:(NSError**)error {
NSDate *date =[NSDate dateWithISO8601String:data];
if(!date && [data length] > 0 && error) {
*error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSDate class]) data:data];
}
return date;
};
-(NSError *)typeMismatchErrorWithExpectedType:(NSString *)expected data:(id)data {
NSString * message = [NSString stringWithFormat:NSLocalizedString(@"Received response [%@] is not an object of type %@",nil),data, expected];
NSDictionary * userInfo = @{NSLocalizedDescriptionKey : message};
return [NSError errorWithDomain:{{classPrefix}}DeserializationErrorDomainKey code:{{classPrefix}}TypeMismatchErrorCode userInfo:userInfo];
}
-(NSError *)emptyValueOccurredError {
NSString * message = NSLocalizedString(@"Received response contains null value in dictionary or array response",nil);
NSDictionary * userInfo = @{NSLocalizedDescriptionKey : message};
return [NSError errorWithDomain:{{classPrefix}}DeserializationErrorDomainKey code:{{classPrefix}}EmptyValueOccurredErrorCode userInfo:userInfo];
}
@end

View File

@ -0,0 +1,45 @@
#import <Foundation/Foundation.h>
/**
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen
* Do not edit the class manually.
*/
/**
* A key for deserialization ErrorDomain
*/
extern NSString *const {{classPrefix}}DeserializationErrorDomainKey;
/**
* Code for deserialization type mismatch error
*/
extern NSInteger const {{classPrefix}}TypeMismatchErrorCode;
/**
* Code for deserialization empty value error
*/
extern NSInteger const {{classPrefix}}EmptyValueOccurredErrorCode;
@protocol {{classPrefix}}ResponseDeserializer <NSObject>
/**
* Deserializes the given data to Objective-C object.
*
* @param data The data will be deserialized.
* @param class The type of objective-c object.
* @param error The error
*/
- (id) deserialize:(id) data class:(NSString *) className error:(NSError**)error;
@end
@interface {{classPrefix}}ResponseDeserializer : NSObject <{{classPrefix}}ResponseDeserializer>
/**
* If an null value occurs in dictionary or array if set to YES whole response will be invalid else will be ignored
* @default NO
*/
@property (nonatomic, assign) BOOL treatNullAsError;
@end

View File

@ -6,7 +6,7 @@ This ObjC package is automatically generated by the [Swagger Codegen](https://gi
- API version: 1.0.0
- Package version:
- Build date: 2016-04-25T18:52:19.055+02:00
- Build date: 2016-05-06T10:30:53.676+02:00
- Build package: class io.swagger.codegen.languages.ObjcClientCodegen
## Requirements
@ -19,7 +19,7 @@ The SDK requires [**ARC (Automatic Reference Counting)**](http://stackoverflow.c
Add the following to the Podfile:
```ruby
pod 'SwaggerClient', :git => 'https://github.com/YOUR_GIT_USR_ID/YOUR_GIT_REPO_ID.git'
pod 'SwaggerClient', :git => 'https://github.com/GIT_USER_ID/GIT_REPO_ID.git'
```
To specify a particular branch, append `, :branch => 'branch-name-here'`

View File

@ -5,7 +5,7 @@
#import "SWGJSONRequestSerializer.h"
#import "SWGQueryParamCollection.h"
#import "SWGConfiguration.h"
#import "SWGResponseDeserializer.h"
/**
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen
@ -29,16 +29,6 @@
*/
extern NSString *const SWGResponseObjectErrorKey;
/**
* A key for deserialization ErrorDomain
*/
extern NSString *const SWGDeserializationErrorDomainKey;
/**
* Code for deserialization type mismatch error
*/
extern NSInteger const SWGTypeMismatchErrorCode;
/**
* Log debug message macro
*/
@ -53,6 +43,7 @@ extern NSInteger const SWGTypeMismatchErrorCode;
/// In order to ensure the HTTPResponseHeaders are correct, it is recommended to initialize one SWGApiClient instance per thread.
@property(nonatomic, readonly) NSDictionary* HTTPResponseHeaders;
@property(nonatomic, strong) id<SWGResponseDeserializer> responseDeserializer;
/**
* Clears Cache
*/
@ -180,15 +171,6 @@ extern NSInteger const SWGTypeMismatchErrorCode;
queryParams:(NSDictionary **)querys
WithAuthSettings:(NSArray *)authSettings;
/**
* Deserializes the given data to Objective-C object.
*
* @param data The data will be deserialized.
* @param class The type of objective-c object.
* @param error The error
*/
- (id) deserialize:(id) data class:(NSString *) class error:(NSError**)error;
/**
* Logs request and response
*

View File

@ -2,10 +2,6 @@
NSString *const SWGResponseObjectErrorKey = @"SWGResponseObject";
NSString *const SWGDeserializationErrorDomainKey = @"SWGDeserializationErrorDomainKey";
NSInteger const SWGTypeMismatchErrorCode = 143553;
static long requestId = 0;
static bool offlineState = false;
static NSMutableSet * queuedRequests = nil;
@ -33,6 +29,7 @@ static void (^reachabilityChangeBlock)(int);
self.requestSerializer = [AFJSONRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.securityPolicy = [self customSecurityPolicy];
self.responseDeserializer = [[SWGResponseDeserializer alloc] init];
// configure reachability
[self configureCacheReachibility];
}
@ -290,154 +287,6 @@ static void (^reachabilityChangeBlock)(int);
[self.reachabilityManager startMonitoring];
}
#pragma mark - Deserialize methods
- (id) deserialize:(id) data class:(NSString *) class error:(NSError **) error {
// return nil if data is nil or class is nil
if (!data || !class) {
return nil;
}
// remove "*" from class, if ends with "*"
if ([class hasSuffix:@"*"]) {
class = [class substringToIndex:[class length] - 1];
}
// pure object
if ([class isEqualToString:@"NSObject"]) {
return data;
}
NSRegularExpression *regexp = nil;
NSTextCheckingResult *match = nil;
NSMutableArray *resultArray = nil;
NSMutableDictionary *resultDict = nil;
NSString *innerType = nil;
// list of models
NSString *arrayOfModelsPat = @"NSArray<(.+)>";
regexp = [NSRegularExpression regularExpressionWithPattern:arrayOfModelsPat
options:NSRegularExpressionCaseInsensitive
error:nil];
match = [regexp firstMatchInString:class
options:0
range:NSMakeRange(0, [class length])];
if (match) {
if(![data isKindOfClass: [NSArray class]]) {
if(error) {
NSDictionary * userInfo = @{NSLocalizedDescriptionKey : NSLocalizedString(@"Received response is not an array", nil)};
*error = [NSError errorWithDomain:SWGDeserializationErrorDomainKey code:SWGTypeMismatchErrorCode userInfo:userInfo];
}
return nil;
}
NSArray *dataArray = data;
innerType = [class substringWithRange:[match rangeAtIndex:1]];
resultArray = [NSMutableArray arrayWithCapacity:[dataArray count]];
[data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id arrObj = [self deserialize:obj class:innerType error:error];
if(arrObj) {
[resultArray addObject:arrObj];
} else {
* stop = YES;
}
}];
return resultArray;
}
// list of primitives
NSString *arrayOfPrimitivesPat = @"NSArray\\* /\\* (.+) \\*/";
regexp = [NSRegularExpression regularExpressionWithPattern:arrayOfPrimitivesPat
options:NSRegularExpressionCaseInsensitive
error:nil];
match = [regexp firstMatchInString:class
options:0
range:NSMakeRange(0, [class length])];
if (match) {
NSArray *dataArray = data;
innerType = [class substringWithRange:[match rangeAtIndex:1]];
resultArray = [NSMutableArray arrayWithCapacity:[dataArray count]];
[data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id arrObj = [self deserialize:obj class:innerType error:error];
if(arrObj) {
[resultArray addObject:arrObj];
} else {
* stop = YES;
}
}];
return resultArray;
}
// map
NSString *dictPat = @"NSDictionary\\* /\\* (.+?), (.+) \\*/";
regexp = [NSRegularExpression regularExpressionWithPattern:dictPat
options:NSRegularExpressionCaseInsensitive
error:nil];
match = [regexp firstMatchInString:class
options:0
range:NSMakeRange(0, [class length])];
if (match) {
NSDictionary *dataDict = data;
NSString *valueType = [class substringWithRange:[match rangeAtIndex:2]];
resultDict = [NSMutableDictionary dictionaryWithCapacity:[dataDict count]];
[data enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
id dicObj = [self deserialize:obj class:valueType error:error];
if(dicObj) {
[resultDict setValue:dicObj forKey:key];
} else {
* stop = YES;
}
}];
return resultDict;
}
// primitives
NSArray *primitiveTypes = @[@"NSString", @"NSDate", @"NSNumber"];
if ([primitiveTypes containsObject:class]) {
if ([class isEqualToString:@"NSString"]) {
return [NSString stringWithString:data];
}
else if ([class isEqualToString:@"NSDate"]) {
return [NSDate dateWithISO8601String:data];
}
else if ([class isEqualToString:@"NSNumber"]) {
// NSNumber from NSNumber
if ([data isKindOfClass:[NSNumber class]]) {
return data;
}
else if ([data isKindOfClass:[NSString class]]) {
// NSNumber (NSCFBoolean) from NSString
if ([[data lowercaseString] isEqualToString:@"true"] || [[data lowercaseString] isEqualToString:@"false"]) {
return [NSNumber numberWithBool:[data boolValue]];
// NSNumber from NSString
} else {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
return [formatter numberFromString:data];
}
}
}
}
// model
Class ModelClass = NSClassFromString(class);
if ([ModelClass instancesRespondToSelector:@selector(initWithDictionary:error:)]) {
return [(JSONModel *) [ModelClass alloc] initWithDictionary:data error:error];
}
return nil;
}
#pragma mark - Operation Methods
- (void) operationWithCompletionBlock: (NSURLRequest *)request
@ -661,7 +510,7 @@ static void (^reachabilityChangeBlock)(int);
else {
[self operationWithCompletionBlock:request requestId:requestId completionBlock:^(id data, NSError *error) {
NSError * serializationError;
id response = [self deserialize:data class:responseType error:&serializationError];
id response = [self.responseDeserializer deserialize:data class:responseType error:&serializationError];
if(!response && !error){
error = serializationError;
}

View File

@ -0,0 +1,45 @@
#import <Foundation/Foundation.h>
/**
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen
* Do not edit the class manually.
*/
/**
* A key for deserialization ErrorDomain
*/
extern NSString *const SWGDeserializationErrorDomainKey;
/**
* Code for deserialization type mismatch error
*/
extern NSInteger const SWGTypeMismatchErrorCode;
/**
* Code for deserialization empty value error
*/
extern NSInteger const SWGEmptyValueOccurredErrorCode;
@protocol SWGResponseDeserializer <NSObject>
/**
* Deserializes the given data to Objective-C object.
*
* @param data The data will be deserialized.
* @param class The type of objective-c object.
* @param error The error
*/
- (id) deserialize:(id) data class:(NSString *) className error:(NSError**)error;
@end
@interface SWGResponseDeserializer : NSObject <SWGResponseDeserializer>
/**
* If an null value occurs in dictionary or array if set to YES whole response will be invalid else will be ignored
* @default NO
*/
@property (nonatomic, assign) BOOL treatNullAsError;
@end

View File

@ -0,0 +1,221 @@
#import "SWGResponseDeserializer.h"
#import <JSONModel/JSONModel.h>
#import <ISO8601/ISO8601.h>
NSString *const SWGDeserializationErrorDomainKey = @"SWGDeserializationErrorDomainKey";
NSInteger const SWGTypeMismatchErrorCode = 143553;
NSInteger const SWGEmptyValueOccurredErrorCode = 143509;
@interface SWGResponseDeserializer ()
@property (nonatomic, strong) NSNumberFormatter* numberFormatter;
@property (nonatomic, strong) NSArray *primitiveTypes;
@property (nonatomic, strong) NSArray *basicReturnTypes;
@property (nonatomic, strong) NSRegularExpression* arrayOfModelsPatExpression;
@property (nonatomic, strong) NSRegularExpression* arrayOfPrimitivesPatExpression;
@property (nonatomic, strong) NSRegularExpression* dictPatExpression;
@property (nonatomic, strong) NSRegularExpression* dictModelsPatExpression;
@end
@implementation SWGResponseDeserializer
- (instancetype)init {
self = [super init];
if (self) {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
_numberFormatter = formatter;
_primitiveTypes = @[@"NSString", @"NSDate", @"NSNumber"];
_basicReturnTypes = @[@"NSObject", @"id"];
_arrayOfModelsPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSArray<(.+)>"
options:NSRegularExpressionCaseInsensitive
error:nil];
_arrayOfPrimitivesPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSArray\\* /\\* (.+) \\*/"
options:NSRegularExpressionCaseInsensitive
error:nil];
_dictPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSDictionary\\* /\\* (.+?), (.+) \\*/"
options:NSRegularExpressionCaseInsensitive
error:nil];
_dictModelsPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSDictionary\\<(.+?), (.+)*\\>"
options:NSRegularExpressionCaseInsensitive
error:nil];
}
return self;
}
#pragma mark - Deserialize methods
- (id) deserialize:(id) data class:(NSString *) className error:(NSError **) error {
// return nil if data is nil or className is nil
if (!data || !className || [data isKindOfClass:[NSNull class]]) {
return nil;
}
// remove "*" from className, if ends with "*"
if ([className hasSuffix:@"*"]) {
className = [className substringToIndex:[className length] - 1];
}
// pure object
if ([self.basicReturnTypes containsObject:className]) {
return data;
}
// primitives
if ([self.primitiveTypes containsObject:className]) {
return [self deserializePrimitiveValue:data class:className error:error];
}
NSTextCheckingResult *match = nil;
// list of models
match = [self.arrayOfModelsPatExpression firstMatchInString:className options:0 range:NSMakeRange(0, [className length])];
if (match) {
NSString *innerType = [className substringWithRange:[match rangeAtIndex:1]];
return [self deserializeArrayValue:data innerType:innerType error:error];
}
// list of primitives
match = [self.arrayOfPrimitivesPatExpression firstMatchInString:className options:0 range:NSMakeRange(0, [className length])];
if (match) {
NSString *innerType = [className substringWithRange:[match rangeAtIndex:1]];
return [self deserializeArrayValue:data innerType:innerType error:error];
}
// map
match = [self.dictPatExpression firstMatchInString:className options:0 range:NSMakeRange(0, [className length])];
if (match) {
NSString *valueType = [className substringWithRange:[match rangeAtIndex:2]];
return [self deserializeDictionaryValue:data valueType:valueType error:error];
}
match = [self.dictModelsPatExpression firstMatchInString:className options:0 range:NSMakeRange(0, [className length])];
if (match) {
NSRange range =[match rangeAtIndex:2];
range = NSMakeRange(range.location, [className length] - range.location -1);
NSString *valueType = [className substringWithRange:range];
return [self deserializeDictionaryValue:data valueType:valueType error:error];
}
// model
Class ModelClass = NSClassFromString(className);
if ([ModelClass instancesRespondToSelector:@selector(initWithDictionary:error:)]) {
return [(JSONModel *) [ModelClass alloc] initWithDictionary:data error:error];
}
return nil;
}
- (id) deserializeDictionaryValue:(id) data valueType:(NSString *) valueType error:(NSError**)error {
if(![data isKindOfClass: [NSDictionary class]]) {
if(error) {
*error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSDictionary class]) data:data];
}
return nil;
}
__block NSMutableDictionary *resultDict = [NSMutableDictionary dictionaryWithCapacity:[data count]];
for (id key in [data allKeys]) {
id obj = [data valueForKey:key];
id dicObj = [self deserialize:obj class:valueType error:error];
if(dicObj) {
[resultDict setValue:dicObj forKey:key];
} else if([obj isKindOfClass:[NSNull class]]) {
if(self.treatNullAsError) {
if (error) {
*error = [self emptyValueOccurredError];
}
resultDict = nil;
break;
}
} else {
resultDict = nil;
break;
}
}
return resultDict;
}
- (id) deserializeArrayValue:(id) data innerType:(NSString *) innerType error:(NSError**)error {
if(![data isKindOfClass: [NSArray class]]) {
if(error) {
*error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSArray class]) data:data];
}
return nil;
}
NSMutableArray* resultArray = [NSMutableArray arrayWithCapacity:[data count]];
for (id obj in data) {
id arrObj = [self deserialize:obj class:innerType error:error];
if(arrObj) {
[resultArray addObject:arrObj];
} else if([obj isKindOfClass:[NSNull class]]) {
if(self.treatNullAsError) {
if (error) {
*error = [self emptyValueOccurredError];
}
resultArray = nil;
break;
}
} else {
resultArray = nil;
break;
}
}
return resultArray;
};
- (id) deserializePrimitiveValue:(id) data class:(NSString *) className error:(NSError**)error {
if ([className isEqualToString:@"NSString"]) {
return [NSString stringWithString:data];
}
else if ([className isEqualToString:@"NSDate"]) {
return [self deserializeDateValue:data error:error];
}
else if ([className isEqualToString:@"NSNumber"]) {
// NSNumber from NSNumber
if ([data isKindOfClass:[NSNumber class]]) {
return data;
}
else if ([data isKindOfClass:[NSString class]]) {
// NSNumber (NSCFBoolean) from NSString
if ([[data lowercaseString] isEqualToString:@"true"] || [[data lowercaseString] isEqualToString:@"false"]) {
return @([data boolValue]);
// NSNumber from NSString
} else {
NSNumber* formattedValue = [self.numberFormatter numberFromString:data];
if(!formattedValue && [data length] > 0 && error) {
*error = [self typeMismatchErrorWithExpectedType:className data:data];
}
return formattedValue;
}
}
}
if(error) {
*error = [self typeMismatchErrorWithExpectedType:className data:data];
}
return nil;
}
- (id) deserializeDateValue:(id) data error:(NSError**)error {
NSDate *date =[NSDate dateWithISO8601String:data];
if(!date && [data length] > 0 && error) {
*error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSDate class]) data:data];
}
return date;
};
-(NSError *)typeMismatchErrorWithExpectedType:(NSString *)expected data:(id)data {
NSString * message = [NSString stringWithFormat:NSLocalizedString(@"Received response [%@] is not an object of type %@",nil),data, expected];
NSDictionary * userInfo = @{NSLocalizedDescriptionKey : message};
return [NSError errorWithDomain:SWGDeserializationErrorDomainKey code:SWGTypeMismatchErrorCode userInfo:userInfo];
}
-(NSError *)emptyValueOccurredError {
NSString * message = NSLocalizedString(@"Received response contains null value in dictionary or array response",nil);
NSDictionary * userInfo = @{NSLocalizedDescriptionKey : message};
return [NSError errorWithDomain:SWGDeserializationErrorDomainKey code:SWGEmptyValueOccurredErrorCode userInfo:userInfo];
}
@end

View File

@ -27,11 +27,28 @@
[formatter setDateFormat:@"yyyy-MM-dd"];
NSDate *date = [formatter dateFromString:dateStr];
NSError* error;
NSDate *deserializedDate = [apiClient deserialize:dateStr class:@"NSDate*" error:&error];
NSDate *deserializedDate = [apiClient.responseDeserializer deserialize:dateStr class:@"NSDate*" error:&error];
XCTAssertNil(error);
XCTAssertEqualWithAccuracy([date timeIntervalSinceReferenceDate], [deserializedDate timeIntervalSinceReferenceDate], 0.001);
}
- (void)testDeserializeInvalidDate {
NSString *dateStr = @"random string";
NSError* error;
NSDate *deserializedDate = [apiClient.responseDeserializer deserialize:dateStr class:@"NSDate*" error:&error];
XCTAssertNotNil(error);
XCTAssertNil(deserializedDate);
}
- (void)testDeserializeEmptyDate {
NSString *dateStr = @"";
NSError* error;
NSDate *deserializedDate = [apiClient.responseDeserializer deserialize:dateStr class:@"NSDate*" error:&error];
XCTAssertNil(error);
XCTAssertNil(deserializedDate);
}
- (void)testDeserializeDateTime {
NSString *dateTimeStr = @"1997-07-16T19:20:30+00:00";
@ -39,7 +56,7 @@
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"];
NSDate *dateTime = [formatter dateFromString:dateTimeStr];
NSError* error;
NSDate *deserializedDateTime = [apiClient deserialize:dateTimeStr class:@"NSDate*" error:&error];
NSDate *deserializedDateTime = [apiClient.responseDeserializer deserialize:dateTimeStr class:@"NSDate*" error:&error];
XCTAssertNil(error);
XCTAssertEqualWithAccuracy([dateTime timeIntervalSinceReferenceDate], [deserializedDateTime timeIntervalSinceReferenceDate], 0.001);
}
@ -47,7 +64,7 @@
- (void)testDeserializeObject {
NSNumber *data = @1;
NSError* error;
NSNumber *result = [apiClient deserialize:data class:@"NSObject*" error:&error];
NSNumber *result = [apiClient.responseDeserializer deserialize:data class:@"NSObject*" error:&error];
XCTAssertNil(error);
XCTAssertEqualObjects(data, result);
}
@ -55,7 +72,7 @@
- (void)testDeserializeString {
NSString *data = @"test string";
NSError* error;
NSString *result = [apiClient deserialize:data class:@"NSString*" error:&error];
NSString *result = [apiClient.responseDeserializer deserialize:data class:@"NSString*" error:&error];
XCTAssertNil(error);
XCTAssertTrue([result isEqualToString:data]);
}
@ -63,12 +80,29 @@
- (void)testDeserializeListOfString {
NSArray *data = @[@"test string"];
NSError* error;
NSArray *result = [apiClient deserialize:data class:@"NSArray* /* NSString */" error:&error];
NSArray *result = [apiClient.responseDeserializer deserialize:data class:@"NSArray<NSString*>*" error:&error];
XCTAssertNil(error);
XCTAssertTrue([result isKindOfClass:[NSArray class]]);
XCTAssertTrue([result[0] isKindOfClass:[NSString class]]);
}
- (void)testDeserializeInvalidListOfNumbers {
NSArray *data = @[@"test string"];
NSError* error;
NSArray *result = [apiClient.responseDeserializer deserialize:data class:@"NSArray<NSNumber*>*" error:&error];
XCTAssertNotNil(error);
XCTAssertNil(result);
}
- (void)testDeserializeListOfNumbers {
NSArray *data = @[@"1.0"];
NSError* error;
NSArray *result = [apiClient.responseDeserializer deserialize:data class:@"NSArray<NSNumber*>*" error:&error];
XCTAssertNil(error);
XCTAssertTrue([result isKindOfClass:[NSArray class]]);
XCTAssertTrue([result[0] isKindOfClass:[NSNumber class]]);
}
- (void)testDeserializeListOfModels {
NSArray *data =
@[
@ -92,7 +126,7 @@
}];
NSError* error;
NSArray *result = [apiClient deserialize:data class:@"NSArray<SWGPet>*" error:&error];
NSArray *result = [apiClient.responseDeserializer deserialize:data class:@"NSArray<SWGPet>*" error:&error];
XCTAssertTrue([result isKindOfClass:[NSArray class]]);
XCTAssertTrue([[result firstObject] isKindOfClass:[SWGPet class]]);
@ -123,7 +157,7 @@
}
};
NSError* error;
NSDictionary *result = [apiClient deserialize:data class:@"NSDictionary* /* NSString, SWGPet */" error:&error];
NSDictionary *result = [apiClient.responseDeserializer deserialize:data class:@"NSDictionary* /* NSString, SWGPet */" error:&error];
XCTAssertTrue([result isKindOfClass:[NSDictionary class]]);
XCTAssertTrue([result[@"pet"] isKindOfClass:[SWGPet class]]);
@ -133,12 +167,45 @@
- (void)testDeserializeNestedMap {
NSDictionary *data =
@{
@"foo": @{
@"bar": @1,
@"bar2": [NSNull null]
}
};
SWGResponseDeserializer* responseDeserializer = [[SWGResponseDeserializer alloc] init];
NSError* error;
NSDictionary *result = [responseDeserializer deserialize:data class:@"NSDictionary* /* NSString, NSDictionary* /* NSString, NSNumber */ */" error:&error];
XCTAssertTrue([result isKindOfClass:[NSDictionary class]]);
XCTAssertTrue([result[@"foo"] isKindOfClass:[NSDictionary class]]);
XCTAssertTrue([result[@"foo"][@"bar"] isKindOfClass:[NSNumber class]]);
}
- (void)testDeserializeNestedMapWithNullValue {
NSDictionary *data =
@{
@"foo": @{
@"bar": @1,
@"bar2": [NSNull null]
}
};
SWGResponseDeserializer* responseDeserializer = [[SWGResponseDeserializer alloc] init];
responseDeserializer.treatNullAsError = YES;
NSError* error;
NSDictionary *result = [responseDeserializer deserialize:data class:@"NSDictionary* /* NSString, NSDictionary* /* NSString, NSNumber */ */" error:&error];
XCTAssertNil(result);
XCTAssertNotNil(error);
}
- (void)testDeserializeNestedMap2 {
NSDictionary *data = @{
@"foo": @{
@"bar": @1
}
};
NSError* error;
NSDictionary *result = [apiClient deserialize:data class:@"NSDictionary* /* NSString, NSDictionary* /* NSString, NSNumber */ */" error:&error];
NSDictionary *result = [apiClient.responseDeserializer deserialize:data class:@"NSDictionary<NSString*, NSDictionary<NSString*, NSNumber*>*>*" error:&error];
XCTAssertTrue([result isKindOfClass:[NSDictionary class]]);
XCTAssertTrue([result[@"foo"] isKindOfClass:[NSDictionary class]]);
@ -148,7 +215,7 @@
- (void)testDeserializeNestedList {
NSArray *data = @[@[@"foo"]];
NSError* error;
NSArray *result = [apiClient deserialize:data class:@"NSArray* /* NSArray* /* NSString */ */" error:&error];
NSArray *result = [apiClient.responseDeserializer deserialize:data class:@"NSArray* /* NSArray* /* NSString */ */" error:&error];
XCTAssertTrue([result isKindOfClass:[NSArray class]]);
XCTAssertTrue([result[0] isKindOfClass:[NSArray class]]);
@ -161,11 +228,11 @@
data = @"true";
NSError* error;
result = [apiClient deserialize:data class:@"NSNumber*" error:&error];
result = [apiClient.responseDeserializer deserialize:data class:@"NSNumber*" error:&error];
XCTAssertTrue([result isEqual:@YES]);
data = @"false";
result = [apiClient deserialize:data class:@"NSNumber*" error:&error];
result = [apiClient.responseDeserializer deserialize:data class:@"NSNumber*" error:&error];
XCTAssertTrue([result isEqual:@NO]);
}

View File

@ -165,7 +165,7 @@ which causes an exception when deserializing the data
SWGTag* tag = [[SWGTag alloc] init];
tag.name = @"tony";
NSLog(@"%@", pet._id);
pet.tags = [[NSArray alloc] initWithObjects:tag, nil];
pet.tags = (id)[[NSArray alloc] initWithObjects:tag, nil];
[api addPetWithBody:pet completionHandler:^(NSError *error) {
if(error) {

View File

@ -8,12 +8,12 @@ git_repo_id=$2
release_note=$3
if [ "$git_user_id" = "" ]; then
git_user_id="YOUR_GIT_USR_ID"
git_user_id="GIT_USER_ID"
echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id"
fi
if [ "$git_repo_id" = "" ]; then
git_repo_id="YOUR_GIT_REPO_ID"
git_repo_id="GIT_REPO_ID"
echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id"
fi