Using JSON to load Objective-C objects
You want to write an iOS or OS X app that quickly and easily retrieves some data from a server and converts it to an Objective-C class. Also, you want to write the server using something standard, like Django or Ruby on Rails.
The standard way that you’d do this would be using NSCoder, where your client application would receive the data and you’d unpack it like so:
However, the file format for serialised objects is internal to iOS and OS X, and is also a binary format that’s very difficult to read while in transit.
We recently had a similar problem, where we wanted to have our server send data in JSON format, which we like because JSON’s pretty human-readable. We could implement our own custom implementation of NSCoder, but we were after a more lightweight solution.
This is where key-value coding comes in. Key-value coding is a feature of Cocoa that lets you access properties and instance variables of class at runtime by name, rather than simply at compile time.
For example, if you had a class that had an NSString property called “name”, you could access it like this:
Or you could access it with key-value coding, like this:
Likewise, you can set the name property like this:
But you could also set it using key-value coding, like this:
The advantage of key-value coding is that you can generate the keys at run-time. Your code doesn’t have to know about the properties and variables kept inside an object in order to attempt to get and set the values.
This is where our lightweight serialisation comes in. Let’s say that we’ve received some JSON data that looks like this:
In this example, we’ve also got an Objective-C object that has matching properties:
Converting the JSON data into a dictionary is pretty easy.
You can then create the Objective-C class from this dictionary by creating a new Employee object, and then iterating over each of the keys in the dictionary, using setValue:forKey: to set the values.
This works even for non-object properties like Employee’s income property, which is an int. In this case, the dictionary loaded from the JSON contains an NSNumber object for the income field; when setValue:forKey: is used for that key, it automatically extracts the int from the NSNumber and applies it to the class.
There’s an even easier way to set all of the values:
Loading objects when you don’t know the class
Another possible use case is one where you receive a chunk of JSON, but you don’t know ahead of time what kind of object the JSON should be turned into. This is where NSClassFromString comes in handy.
If your JSON object contains a field called, say, class, you can use that to create the empty object. Like so:
The reason we use NSObject in the above example is because all NSObjects support the key-value coding methods. Even though loadedObject is treated as a generic NSObject, the fact that it was created with whatever class ObjectClass is determined to be means that the object will be what you specify.
Dangers and Caveats
This technique only applies for simple properties, like strings and numbers. More complex data types or references to other Objective-C objects need to be handled differently.
If you use setValue:forKey: on a key that doesn’t exist in the target object, the object will throw an exception and crash. There are a couple of ways you can handle this; one is to implement the setValue:forUndefinedKey: method in your classes.
This method is called when you try to set a value for a property or instance variable that doesn’t exist. The default implementation throws an exception and crashes; you can implement your own that leaves a warning, for example.
A final note: this is a very quick-and-dirty solution. If you want more reliable behaviour, you’ll need to add more checks and validation code. For simply loading objects, it works pretty well.