Skip to content

An Objective-C library to convert model objects to PropertyLists/JSON Dictionaries, and vice versa.

License

Notifications You must be signed in to change notification settings

ChristianOrgler/JAGPropertyConverter

 
 

Repository files navigation

JAGPropertyConverter

JAGPropertyConverter is a library to allow easy serialization/deserialization to/from JSON or PropertyList formats.

Overview

JAGPropertyConverter allows you to convert a JSON dictionary such as

{ 
    "userID" : 1234,
    "name" : "Jane Smith",
    "likes" : ["swimming", "movies", "tennis"],
    "invitedBy" : { 
        "userID" : 9876,
        "name" : "Bob Willis"
    },
    "friends" : [
        { "userID" : 8873, "name" : "Jodi Fischer" },
        { "userID" : 9876, "name" : "Bob Willis" }
    ]

}

to/from Objective-C "model" classes such as:

@interface User : NSObject
    @property (assign)  int         userID;
    @property (copy)    NSString    *name;
    @property (strong)  NSArray     *likes;
    @property (strong)  User        *invitedBy;
    @property (strong)  NSSet       *friends;
@end

It does this by using the objc runtime library to discover which properties are defined on an object, and then using Key-Value coding to set/retrieve the values.

It will convert recursively, so a model's dictionary of models with array properties of further models will be handled correctly.

There are three "outputTypes" that the converter supports when converting from a model to an NSDictionary: Full, PropertyList, and JSON.

  • Full output "converts" any unrecognized NSObject subclass to itself. Thus the NSDictionary would have a value of that object.
  • PropertyList output drops any unrecognized NSObject subclass, so that the resultant NSDictionary is PropertyList-compliant. (But see the section on NSURL below)
  • JSON output drops any unrecognized NSObject subclass, so that the resultant NSDictionary is JSON-compliant. (But see the sections on NSURL and NSDate below).

Models

Converterting from a Model to an NSDictionary is relatively straightforward, using the property name as a key and the property value as a value. Converting from an NSDictionary to a model requires an important first step of recognizing what Model class the NSDictionary represents. JAGPropertyConverter has an "identifyDict" block property that checks any NSDictionary value, and if it returns a Class, the converter attempts to convert the NSDictionary into that class. If identifyDict returns nil, the converter leaves the NSDictionary unchanged.

To determine which NSObject subclasses are considered "Models" (i.e., which it should convert), JAGPropertyConverter relies on its classesToConvert property. Objects which are subclasses of a Class in classesToConvert are converted.

By default, weak/assign object pointers are not converted (but assign properties for scalars are). This is because weak references often indicate a retain loop (eg, between an object and its delegate), which would lead to cycle in the object graph and thence an infinite loop in the conversion. This property can be controlled by the "shouldConvertWeakProperties" in JAGPropertyConverter.

NSURL

NSURL properties are not technically valid for JSON or ProperyLists, so JAGPropertyConverter serializes/deserializes them using the string of the absolute path.

NSDate

NSDate properties are not valid for JSON, and different use cases will call for different serialization methods. We allow for this by the convertToDate and convertFromDate block properties. They are called when converting to/from NSDate properties with JSON output type.

NSObject properties

NSObject itself has some properties. JAGPropertyFinder ignores these. If there is need in the future, JAGPropertyFinder could take a setting determining whether it ignores or finds those properties.

NSSet

JAGPropertyConverter converts arrays to sets and vice-versa, as needed.

Example Usage

//Serialization
MyModel *model = [MyModel populatedModel];
JAGPropertyConverter *converter = [[JAGPropertyConverter alloc] initWithOutputType:kJAGJSONOutput];
converter.classesToConvert = [NSSet setWithObject:[MyModel class]];
NSDictionary *jsonDictionary = [converter convertToDictionary:model];

//Deserialization
NSDictionary *jsonDictionary = [NSDictionary dictionaryWithContentsOfFile:@"/path/to/model.json"];
JAGPropertyConverter *converter = [[JAGPropertyConverter alloc] init];
converter.identifyDict = ^(NSDictionary *dict) {
    if ([dict valueForKey:@"userID"]) {
        return [User class];
    } else if ([dict valueForKey:@"primaryKey"]) {
        return [MyModel class];
    }
    return nil;    
}
MyModel *model = [converter composeModelFromObject:jsonDictionary];

The converter can also handle NSArray, NSSet, and NSDictionary inputs as well. To see an extensive set of code samples, look at JAGPropertyConverterTests/ExampleTest.m .

Things to do

Since JAGPropertyConverter uses Key-Value coding to get/set values, it doesn't respect custom getters and setters with non-standard names. JAGProperty has this ability, so we could in theory support this. Two things have dissuaded us so far. The first is that ARC produces warnings, since you are invoking an unknown (to it) selector to get/set properties, so it can't ensure memory management is handled correctly. The second is that Key-Value coding handles scalars decently well, which would take a little more work to do when directly using the properties getters and setters.

While Key-Value coding handles struct (and similar) scalars decently well, we have not yet enabled JAGPropertyConverter to parse them into a JSON-value format.

Known Bugs

JAGPropertyConverter doesn't handle NSOrderedSet, NSCountedSet or custom subclasses of NSArray/NSDictionary very well. You may find them composed into a vanilla NSSet/NSArray/NSDictionary. Support for the less-common Apple-supplied classes can be implemented when it's needed, but custom subclasses would require a bit more work.

Decomposed BOOL properies will serialize under JSONKit to 0s and 1s. This is actually due to a bug in Apple's Key-Value coding. The documentation claims that it converts aBool into [NSNumber numberWithBool:aBool], but in fact it looks like it uses [NSNumber numberWithInt:aBool] or similar. JSONKit (correctly) views this as an integer, not a boolean.

Requirements

JAGPropertyConverter requires iOS 4.0 or higher, and uses ARC. In theory it should also work with OS X 10.6 or higher, but so far it has only been tested for iOS development.

ExampleTest.m uses the literal expressions introduced in Xcode 4.4. Building that particular test requires Xcode 4.4, but all other files (including all files in the library proper) should work for any version of Xcode >4.0.

Credits

JAGPropertyConverter was created by James Gill in the development of SpotNote. The intial code for JAGProperty came from Mike Ash's RTProperty class, in his excellent runtime libraries. The documentation was generated by appledoc, and the tag versioning follows Semantic Versioning.

License

JAGPropertyConverter is available under the MIT license. See the LICENSE file for more info.

About

An Objective-C library to convert model objects to PropertyLists/JSON Dictionaries, and vice versa.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Objective-C 99.2%
  • Ruby 0.8%