JSON Pattern
I’m not sure I have the best pattern for serializing and de-serializing model objects as JSON.
I’ll describe what I have.
QSAPIObject Protocol
Some (but not all) of my model objects can be represented as JSON. Those model objects are not subclasses of anything (by design) — but they do implement a couple protocols, including the QSAPIObject protocol.
This protocol requires two methods:
- (NSDictionary \*)JSONRepresentation;
+ (instancetype)objectWithJSONRepresentation:(NSDictionary \*)JSONRepresentation;
This part makes sense, I think. There’s no shared code in JSON serialization/deserialization, so it makes sense as a protocol as opposed to a subclass.
But here’s where it gets tricky.
Collections
I want some common code to handle arrays — I want JSONArrayWithObjects and objectsWithJSONArray.
These methods are exactly the same for all model objects: they create an array, and populate it by calling the QSAPIObject protocol methods.
What I did: I created a QSAPIObject class (in the same file as the protocol with the same name) and gave it two class methods:
+ (NSArray \*)JSONArrayWithObjects:(NSArray \*)objects;
+ (NSArray \*)objectsWithJSONArray:(NSArray \*)JSONArray class:(Class<QSAPIObject>)class;
So, to turn VSNote objects into JSON dictionaries, I do this:
NSArray \*JSONNotes = [QSAPIObject JSONArrayWithObjects:notes];
And to turn JSON dictionaries into VSNote objects:
NSArray \*notes = [QSAPIObject objectsWithJSONArray:JSONArray class:[VSNote class]];
This isn’t the worst thing. It works perfectly well. But I don’t particularly love calling class methods — why not just make them C methods?
NSArray \*JSONNotes = QSJSONArrayWithObjects(notes);
As C methods, there’s one entity (a function name) rather than two (class and method), and I like that. But, on the other hand, using C methods for things like this can be considered a code smell — it’s a sign that we’ve stepped outside of our object-oriented world. (Same with class methods, but with C it’s more obvious.)
Another thing I don’t like is that class parameter to objectsWithJSONArray. That’s a dead give-away that something’s gone wrong here in object-land.
I could add these collection methods to the QSAPIObject protocol, and make the model objects implement these. Then I’d have something nicer like:
NSArray \*notes = [VSNote objectsWithJSONArray:JSONArray];
NSArray \*JSONNotes = [VSNote JSONArrayWithNotes:notes];
That’s a more ideal API, for sure. But then the model objects are duplicating code — unless those methods are just one-liners that call the common implementations in the QSAPIObject class methods.
And if I do that, I’m basically adding boilerplate code to my model objects that isn’t strictly necessary, that would be there just so I could feel a little better about the API. Calling the QSAPIObject class methods is perfectly work-able, if un-lovely.
Hmmm.