objc.ResponseDeserializer-body.mustache Maven / Gradle / Ivy
#import "{{classPrefix}}ResponseDeserializer.h"
#import
#import
NSString *const {{classPrefix}}DeserializationErrorDomainKey = @"{{classPrefix}}DeserializationErrorDomainKey";
NSInteger const {{classPrefix}}TypeMismatchErrorCode = 143553;
NSInteger const {{classPrefix}}EmptyValueOccurredErrorCode = 143509;
NSInteger const {{classPrefix}}UnknownResponseObjectErrorCode = 143528;
@interface {{classPrefix}}ResponseDeserializer ()
@property (nonatomic, strong) NSNumberFormatter* numberFormatter;
@property (nonatomic, strong) NSArray *primitiveTypes;
@property (nonatomic, strong) NSArray *basicReturnTypes;
@property (nonatomic, strong) NSArray *dataReturnTypes;
@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"];
_dataReturnTypes = @[@"NSData"];
_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 {
if (!data || !className) {
return nil;
}
if ([className hasSuffix:@"*"]) {
className = [className substringToIndex:[className length] - 1];
}
if([self.dataReturnTypes containsObject:className]) {
return data;
}
id jsonData = nil;
if([data isKindOfClass:[NSData class]]) {
jsonData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:error];
} else {
jsonData = data;
}
if(!jsonData) {
jsonData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
} else if([jsonData isKindOfClass:[NSNull class]]) {
return nil;
}
// pure object
if ([self.basicReturnTypes containsObject:className]) {
return jsonData;
}
// primitives
if ([self.primitiveTypes containsObject:className]) {
return [self deserializePrimitiveValue:jsonData class:className error:error];
}
NSTextCheckingResult *match = nil;
NSRange range = NSMakeRange(0, [className length]);
// list of models
match = [self.arrayOfModelsPatExpression firstMatchInString:className options:0 range:range];
if (match) {
NSString *innerType = [className substringWithRange:[match rangeAtIndex:1]];
return [self deserializeArrayValue:jsonData innerType:innerType error:error];
}
// list of primitives
match = [self.arrayOfPrimitivesPatExpression firstMatchInString:className options:0 range:range];
if (match) {
NSString *innerType = [className substringWithRange:[match rangeAtIndex:1]];
return [self deserializeArrayValue:jsonData innerType:innerType error:error];
}
// map
match = [self.dictPatExpression firstMatchInString:className options:0 range:range];
if (match) {
NSString *valueType = [className substringWithRange:[match rangeAtIndex:2]];
return [self deserializeDictionaryValue:jsonData valueType:valueType error:error];
}
match = [self.dictModelsPatExpression firstMatchInString:className options:0 range:range];
if (match) {
NSString *valueType = [className substringWithRange:[match rangeAtIndex:2]];
return [self deserializeDictionaryValue:jsonData valueType:valueType error:error];
}
// model
Class ModelClass = NSClassFromString(className);
if ([ModelClass instancesRespondToSelector:@selector(initWithDictionary:error:)]) {
return [(JSONModel *) [ModelClass alloc] initWithDictionary:jsonData error:error];
}
if(error) {
*error = [self unknownResponseErrorWithExpectedType:className data:jsonData];
}
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 stringWithFormat:@"%@",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];
}
-(NSError *)unknownResponseErrorWithExpectedType:(NSString *)expected data:(id)data {
NSString * message = [NSString stringWithFormat:NSLocalizedString(@"Unknown response expected type %@ [response: %@]",nil),expected,data];
NSDictionary * userInfo = @{NSLocalizedDescriptionKey : message};
return [NSError errorWithDomain:{{classPrefix}}DeserializationErrorDomainKey code:{{classPrefix}}UnknownResponseObjectErrorCode userInfo:userInfo];
}
@end