-
Notifications
You must be signed in to change notification settings - Fork 0
Deserialization of Dictionaries
ClassMapper allows you to convert NSDictionaries into your objects. The purpose of this is to allow you to more easily bridge between your models and your external serialization (e.g. JSON).
In order to deserialize into one of your objects, your class must be KVC compliant. For each key in the dictionary of serialized data, ClassMapper will set the value fo the property. Let's look at a simple example, using only scalars from the supported class list.
Let's suppose you have an object for your Social Beer program:
@interface Beer : NSObject
@property(nonatomic, strong)NSString *name;
@property(nonatomic, strong)NSNumber *rank;
@endIf we want to use ClassMapper to create an instance of Beer with the name "Pabst Blue Ribbon" and the rank 1, we can run the following:
// Equivalent to Beer *beer = [Beer new]; beer.name=@"Pabst Blue Ribbon"; beer.rank = [NSNumber numberWithInt:1];
[ClassMapper deserialize:[NSDictionary dictionaryWithObjectsAndKeys:@"Pabst Blue Ribbon", @"name",
[NSNumber numberWithInt:1], @"rank", nil]
toClass:[Beer class]];And voila, we have our object.
Now, let's add another model into the mix:
@interface User : NSObject
@property(nonatomic, strong)NSString *name;
@property(nonatomic, strong)Beer *beer;
@endThe good news here is that the naive solution works. If we try:
NSDictionary *beer = [NSDictionary dictionaryWithObjectsAndKeys:@"Pabst Blue Ribbon",
@"name", [NSNumber numberWithInt:1], @"rank", nil];
// Equiv to {"beer":{"name":"Pabst Blue Ribbon", "rank":1}, "name":"He-Man"}
NSDictionary *user = [NSDictionary dictionaryWithObjectsAndKeys:beer, @"beer", @"He-Man", @"name", nil];
[ClassMapper deserialize:user toClass:[User class]];We get back an instance of User user, where user.beer is an instance of the Beer class just like we created above. ClassMapper will read your property meta-data and create instances of the appropriate type for you.
You can also use dictionaries within your classes. Let's say we had a class like:
// Note: this is a very bad design, but a useful example, like armadillos
@interface Name : NSObject
@property(nonatomic, strong)NSDictionary *parts;
@endWhich you use to create instances like so:
Name *name = [Name new];
name.parts = [NSDictionary dictionaryWithObjectsAndKeys:@"Conan", @"first", @"the", @"middle",
@"Barbarian", @"last", nil];We can create the same instance using ClassMapper by just nesting our dictionaries:
NSDictionary *parts = [NSDictionary dictionaryWithObjectsAndKeys:@"Conan", @"first", @"the", @"middle",
@"Barbarian", @"last", nil];
NSDictionary *nameDict = [NSDictionary dictionaryWithObjectsAndKeys:parts, @"parts"];
[ClassMapper deserialize:nameDict toClass:[Name class]];ClassMapper reads the meta information about the parts property, and just like before creates a new NSDictionary with that state.
Unfortunately, NSDictionaries don't have the same metadata that your classes do, so we have a bit of an issue if we have complex objects inside the dictionary. The above example works because we only use NSStrings, which don't have any objects inside of them (and are thus terminal nodes in (de)serialization). If we encounter a dictionary inside of another dictionary, then we get some ambiguity. For instance:
@interface SocialNetworkExample : NSObject
@property(nonatomic, strong)NSString *ex;
@end
@interface Keyword : NSObject
@property(nonatomic, strong)NSDictionary *networkExamples;
@end
NSDictionary *twitterExDict = [NSDictionary dictionaryWithObjectsAndKeys:@"boo #sopa", @"ex", nil];
NSDictionary *examplesDict = [NSDictionary dictionaryWithObjectsAndKeys:twitterExDict, @"Twitter", nil];
// Equivalent to {"networkExamples":{"Twitter":{"ex":"boo #sopa"}}}
NSDictionary *keywordDict = [NSDictionary dictionaryWithObjectsAndKeys:examplesDict, @"networkExamples", nil];
Keyword *keyword = [ClassMapper deserialize:keywordDict toClass:[Keyword class]];
id ex = [keyword.networkExamples objectForKey:@"Twitter"];So is ex an instance NSDictionary or an instance of SocialNetworkExample? In this case, it will be an NSDictionary. But, we can use the MapperConfig to help us here!
[[MapperConfig sharedInstance] mapKey:@"Twitter" toClass:[SocialNetworkExample class]];
Keyword *keyword = [ClassMapper deserialize:keywordDict toClass:[Keyword class]];
SocialNetworkExample *ex = [keyword.networkExamples objectForKey:@"Twitter"];
// ex.ex == @"boo #sopa"Now this only really helps us when we can predict the keys. ClassMapper currently doesn't support dictionary level type information, so for now, if you don't know the keys in advance you'll have to stick with dictionaries.