Created
March 17, 2012 05:53
-
-
Save juliengrimault/2055538 to your computer and use it in GitHub Desktop.
NSObject category for handling JSON dictionaries. Described in detail at http://www.cimgf.com/2012/01/11/handling-incoming-json-redux/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // NSObject+setValuesForKeysWithJSONDictionary.h | |
| // SafeSetDemo | |
| // | |
| // Created by Tom Harrington on 12/29/11. | |
| // Copyright (c) 2011 Atomic Bird, LLC. All rights reserved. | |
| // | |
| // Modified by Julien Grimault | |
| // | |
| #import <Foundation/Foundation.h> | |
| @interface NSObject (setValuesForKeysWithJSONDictionary) | |
| /* | |
| for each property of self, a lookup is made on the |propertiesMapping| dictionary to get the key to use in the |keyedValues| dictionary. If no entry is found in propertiesMapping, the property name is used directly. | |
| propertiesMapping uses the property name as key and the keyedValues key as value. | |
| */ | |
| - (void)setValuesForKeysWithJSONDictionary:(NSDictionary *)keyedValues | |
| propertiesMapping:(NSDictionary*)propertiesMapping | |
| dateFormatter:(NSDateFormatter *)dateFormatter; | |
| - (void)setValuesForKeysWithJSONDictionary:(NSDictionary *)keyedValues | |
| dateFormatter:(NSDateFormatter *)dateFormatter; | |
| @end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // NSObject+setValuesForKeysWithJSONDictionary.m | |
| // SafeSetDemo | |
| // | |
| // Created by Tom Harrington on 12/29/11. | |
| // Copyright (c) 2011 Atomic Bird, LLC. All rights reserved. | |
| // | |
| // Modified by Julien Grimault | |
| // | |
| #import "NSObject+setValuesForKeysWithJSONDictionary.h" | |
| #import <objc/runtime.h> | |
| #import <objc/message.h> | |
| @implementation NSObject (setValuesForKeysWithJSONDictionary) | |
| - (void)setValuesForKeysWithJSONDictionary:(NSDictionary *)keyedValues | |
| dateFormatter:(NSDateFormatter *)dateFormatter | |
| { | |
| return [self setValuesForKeysWithJSONDictionary:keyedValues | |
| propertiesMapping:nil | |
| dateFormatter:dateFormatter]; | |
| } | |
| - (void)setValuesForKeysWithJSONDictionary:(NSDictionary *)keyedValues | |
| propertiesMapping:(NSDictionary*)propertiesMapping | |
| dateFormatter:(NSDateFormatter *)dateFormatter | |
| { | |
| //we need to go up the inheritance tree to assign the properties declared in the superclass | |
| //we must however assign the properties starting with the root class and ending with [self class], | |
| //this is to ensure that the overriden properties are assigned correctly | |
| NSMutableArray* inheritanceTree = [NSMutableArray array]; | |
| Class currentClass = [self class]; | |
| while (currentClass) { | |
| [inheritanceTree addObject:currentClass]; | |
| currentClass = class_getSuperclass(currentClass); | |
| } | |
| [inheritanceTree enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(Class objectClass, NSUInteger idx, BOOL* stop){ | |
| unsigned int propertyCount; | |
| objc_property_t *properties = class_copyPropertyList(objectClass, &propertyCount); | |
| /* | |
| This code iterates over self's properties instead of ivars because the backing ivar might have a different name | |
| than the property, for example if the class includes something like: | |
| @synthesize foo = foo_; | |
| In this case what we really want is "foo", not "foo_", since the incoming keys in keyedValues probably | |
| don't have the underscore. Looking through properties gets "foo", looking through ivars gets "foo_". | |
| */ | |
| for (int i=0; i<propertyCount; i++) { | |
| objc_property_t property = properties[i]; | |
| const char *propertyName = property_getName(property); | |
| NSString* keyName = [NSString stringWithUTF8String:propertyName]; | |
| //apply mapping | |
| NSString *keyNameJSON = keyName; //default if there is no mapping provided | |
| if (propertiesMapping) { | |
| NSString* mapping = [propertiesMapping objectForKey:keyName]; | |
| if (mapping) { | |
| keyNameJSON = mapping; | |
| } | |
| } | |
| id value = [keyedValues objectForKey:keyNameJSON]; | |
| if (value != nil) { | |
| char *typeEncoding = NULL; | |
| typeEncoding = property_copyAttributeValue(property, "T"); | |
| if (typeEncoding == NULL) { | |
| continue; | |
| } | |
| switch (typeEncoding[0]) { | |
| case '@': | |
| { | |
| // Object | |
| Class propertyClass = nil; | |
| if (strlen(typeEncoding) >= 3) { | |
| char *propertyClassName = strndup(typeEncoding+2, strlen(typeEncoding)-3); | |
| propertyClass = NSClassFromString([NSString stringWithUTF8String:propertyClassName]); | |
| free(propertyClassName); | |
| } | |
| // Check for type mismatch, attempt to compensate | |
| if ([propertyClass isSubclassOfClass:[NSString class]] && [value isKindOfClass:[NSNumber class]]) { | |
| value = [value stringValue]; | |
| } else if ([propertyClass isSubclassOfClass:[NSNumber class]] && [value isKindOfClass:[NSString class]]) { | |
| //check if the value is yes,true,no or false.. | |
| if ([value caseInsensitiveCompare:@"yes"] == NSOrderedSame || | |
| [value caseInsensitiveCompare:@"true"] == NSOrderedSame) { | |
| value = [NSNumber numberWithBool:YES]; | |
| } else if ([value caseInsensitiveCompare:@"no"] == NSOrderedSame || | |
| [value caseInsensitiveCompare:@"false"] == NSOrderedSame) { | |
| value = [NSNumber numberWithBool:NO]; | |
| } else { | |
| // If the ivar is an NSNumber we really can't tell if it's intended as an integer, float, etc. | |
| NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; | |
| [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; | |
| value = [numberFormatter numberFromString:value]; | |
| } | |
| } else if ([propertyClass isSubclassOfClass:[NSDate class]] && [value isKindOfClass:[NSString class]]) { | |
| if (dateFormatter) { | |
| value = [dateFormatter dateFromString:value]; | |
| } else { | |
| NSTimeInterval interval = [value doubleValue]; | |
| if (interval != 0.0) { | |
| value = [NSDate dateWithTimeIntervalSince1970:interval]; | |
| } | |
| } | |
| } else if ([propertyClass isSubclassOfClass:[NSURL class]] && [value isKindOfClass:[NSString class]]) { | |
| value = [NSURL URLWithString:value]; | |
| } | |
| break; | |
| } | |
| case 'i': // int | |
| case 's': // short | |
| case 'l': // long | |
| case 'q': // long long | |
| case 'I': // unsigned int | |
| case 'S': // unsigned short | |
| case 'L': // unsigned long | |
| case 'Q': // unsigned long long | |
| case 'f': // float | |
| case 'd': // double | |
| case 'B': // BOOL | |
| { | |
| if ([value isKindOfClass:[NSString class]]) { | |
| NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; | |
| [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; | |
| value = [numberFormatter numberFromString:value]; | |
| } | |
| break; | |
| } | |
| case 'c': // char | |
| case 'C': // unsigned char | |
| { | |
| if ([value isKindOfClass:[NSString class]]) { | |
| char firstCharacter = [value characterAtIndex:0]; | |
| value = [NSNumber numberWithChar:firstCharacter]; | |
| } | |
| break; | |
| } | |
| default: | |
| { | |
| DDLogError(@"Unknown type engonding %c",typeEncoding[0]); | |
| break; | |
| } | |
| } | |
| [self setValue:value forKey:keyName]; | |
| free(typeEncoding); | |
| } | |
| } | |
| free(properties); | |
| }]; | |
| } | |
| @end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment