-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathNSObject+setValuesForKeysWithJSONDictionary.m
120 lines (104 loc) · 5.03 KB
/
NSObject+setValuesForKeysWithJSONDictionary.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//
// NSObject+setValuesForKeysWithJSONDictionary.m
// SafeSetDemo
//
// Created by Tom Harrington on 12/29/11.
// Copyright (c) 2011 Atomic Bird, LLC. All rights reserved.
//
#import "NSObject+setValuesForKeysWithJSONDictionary.h"
#import <objc/runtime.h>
@implementation NSObject (setValuesForKeysWithJSONDictionary)
- (void)setValuesForKeysWithJSONDictionary:(NSDictionary *)keyedValues dateFormatter:(NSDateFormatter *)dateFormatter
{
unsigned int propertyCount;
objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
/*
This code starts by 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 probably 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_".
If there's no property name that matches the incoming key name, the code checks the ivars as well.
*/
for (int i=0; i<propertyCount; i++) {
objc_property_t property = properties[i];
const char *propertyName = property_getName(property);
NSString *keyName = [NSString stringWithUTF8String:propertyName];
// See if the property name is being supplied in the JSON dictionary.
id value = [keyedValues objectForKey:keyName];
// If not, see if the backing ivar name is being supplied in the JSON dictionary.
if (value == nil) {
const char *ivarPropertyName = property_copyAttributeValue(property, "V");
NSString *ivarName = [NSString stringWithUTF8String:ivarPropertyName];
value = [keyedValues objectForKey: ivarName];
free((void *)ivarPropertyName);
}
if (value != nil) {
char *typeEncoding = NULL;
typeEncoding = property_copyAttributeValue(property, "T");
if (typeEncoding == NULL) {
continue;
}
switch (typeEncoding[0]) {
case '@':
{
// Object
Class class = nil;
if (strlen(typeEncoding) >= 3) {
char *className = strndup(typeEncoding+2, strlen(typeEncoding)-3);
class = NSClassFromString([NSString stringWithUTF8String:className]);
free(className);
}
// Check for type mismatch, attempt to compensate
if ([class isSubclassOfClass:[NSString class]] && [value isKindOfClass:[NSNumber class]]) {
value = [value stringValue];
} else if ([class isSubclassOfClass:[NSNumber class]] && [value isKindOfClass:[NSString class]]) {
// 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 ([class isSubclassOfClass:[NSDate class]] && [value isKindOfClass:[NSString class]] && (dateFormatter != nil)) {
value = [dateFormatter dateFromString: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:
{
break;
}
}
[self setValue:value forKey:keyName];
free(typeEncoding);
}
}
free(properties);
}
@end