Is RestKit the only framework that has JSON to Objective-C objects mapping? - objective-c

I am looking for a library or framework that does JSON to Objective-C relational object mapping.
i.e. I need to map JSON containing objects, array of objects and dictionaries of objects to my custom objects.
something like:
DataObject {
"user" : {
"name":"Peter",
"id":1234
}
"place": "UK"
"job": {
"title" : "CTO",
"salary" : 1234567
}
"employess": [
{
"name":"Carlton",
"id":1235
},
{
"name":"Hugo",
"id":12346
}]
}
So there is a DataObject a UserObject and an employees array consisting of UserObjects.
I would like for the mapping from the JSON to my DataObject to happen "automatically", of course meant as I would like to describe the objects and there relations in the Object class and have the mapping done from this, instead of manually mapping each nested object.
(First level native objective-c properties are easily done with setValue:forKey and other KVO methods, but from there on it gets complicated).
I have been testing out RestKit but it seems there is no way to pick and choose which functionality you need from that framework, it is either all of it or none of it, and I do find it does too much for my needs.
Are anyone familiar with a library etc. out there that can do this?
Thank you in advance.

To map JSON to Objective-C objects, I have tried RestKit. I used it a while ago, so my criticisms might not apply anymore.
What I found out: nice idea. Not too complicated to plug-in. If it works, great for you. But if not, or if you need to push it a bit further, good luck to debug.
I regret the time I invested in it.
I am only looking for JSON to Objective-C objects, not the other way around.
I found a link on SO to JTObjectMapping - but can't find the original answer. Seems more lightweight and close to what I was searching, but I did not had the opportunity to try it.
An other similar class: jastor.
I prefer the approach of this two classes over RestKit, as it only takes care of one job, whereas RestKit tried to handle everything.

What you have posted above isn't valid JSON. If you made it valid JSON what you want to do is impossible without a specific schema, eg.
{
"DataObject": {
"user": {
"name": "Peter",
"id": 1234
},
"place": "UK",
"job": {
"title": "CTO",
"salary": 1234567
}
}
}
Is Dataobject a dictionary or an Object? What about User or Job? What is User is an instance of NSUser and job is an NSDictionary?
On the other hand, if you have a known schema:-
[
{
"class": "DataObject",
"properties": {
"user": {
"class": "User",
"properties": {
"name": "Peter",
"id": 1234
}
},
"place": "UK",
"job": {
"title": "CTO",
"salary": 1234567
}
}
}
]
you don't need a framework as it is trivial to map to your objects yourself once you have valid JSON. Pseudocode:-
createWithDict(dict) {
var newOb = create new(dict["class"]);
dict.properties.each( val, key ) {
if(val is dictionary && val.hasproperty("class"))
val = createWithDict(val)
newOb[key] = val
}
return newOb;
}

Related

JSON Schema - field named "type"

I have an existing JSON data feed between multiple systems which I do not control and cannot change. I have been tasked with writing a schema for this feed. The existing JSON looks in part like this:
"ids": [
{ "type": "payroll", "value": "011808237" },
{ "type": "geid", "value": "31826" }
]
When I try to define a JSON schema for this, I wind up with a scrap of schema that looks like this:
"properties": {
"type": { <====================== PROBLEM!!!!
"type": "string",
"enum": [ "payroll", "geid" ]
},
"value": {
"type": [ "string", "null" ],
"pattern": "^[0-9]*$"
}
}
As you might guess, when the JSON validator hits that "type" on the line marked "PROBLEM!!!" it gets upset and throws an error about how type needs to be a string or array.
That's a bug in the particular implementation you're using, and should be reported as such. It should be able to handle properties-that-look-like-keywords just fine. In fact, the metaschema (the schema to valid schemas) uses "type" in exactly this way, along with all the other keywords too: e.g. http://json-schema.org/draft-07/schema
I wonder if it is not making use of the official test suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite)?
You didn't indicate what implementation you're using, or what language, but perhaps you can find an alternative one here: https://json-schema.org/implementations.html#validators
I found at least a work-around if not a proper solution. Instead of "type" inside "properties", use "^type$" inside "patternProperties". I.e.
"patternProperties": {
"^type$": {
"type": "string"
}
}
Unfortunately there doesn't seem to be a great way to make "^type$" a required property. I've settled for listing all the other properties as "required" and setting the min and max property counts to the number that should be there.

RestKit resolve objects given an id or partial representation in JSON

I have a JSON array, something like:
[{
"name": "John Smith",
"occupationId": 3
},
{
"name": "Steven Davis",
"occupationId": 2
}
]
The occupation response looks something like:
[{
"id": 2,
"name": "Teacher"
},
{
"id": 3,
"name": "Teaching Assistant"
}
]
Is there a way to allow RestKit to request the correct data for the occupations, given only their id? I know this can be done if the data is persisted using CoreData, via the addConnectionForRelationship:connectedBy: method, but I would rather that the data is transient, given that the server is local and there really is no need to persist the data. I'm also aware that RKObjectMapping does not support the identifiactionAttributes property, meaning I cannot (to my knowledge) designate a way to allow the class to declare a unique, identifying property.
Any help would be appreciated. I am using a mix of Objective-C and Swift, and as such, I do not mind answers in either language.

RestKit: Composing Relationships with the Nil Key Path

I have two classes
Author with attributes id, papers (Paper relationship), ...
Paper with attributes id, mainAuthor (Author relationship), authors (Author relationship) ...
and want to map some JSON to it
"authors": [
{
"id": 123,
"papers": [
{
"id": 1,
"main_author_id": 123,
...
},
...
]
},
...
]
The problem is that the JSON is not structured like
"authors": [
{
"id": 123,
"papers": [
{
"id": 1,
"main_author": {
"id": 123
}
...
},
...
]
},
...
]
so that I could easily map things (note the *main_author* part of both JSON examples). I tried using mapping this value without a key path as explained here:
[authorMapping addAttributeMappingToKeyOfRepresentationFromAttribute:#"main_author_id"];
[authorMapping addAttributeMappingsFromDictionary:#{#"(main_author_id)": #"id"}];
but I'm getting an error telling me that the keyPath id already exists and I may not add a second mapping for this keyPath. I totally understand this error, but I have no idea how to map from *main_author_id* back to id. Changing the data source may be the best solution, but this is unfortunately not possible.
Any suggestion is highly welcome! Thanks!
This is exactly what foreign key mapping is for. It allows you to temporarily store the 'identity' value that you're provided with and then RestKit will find the appropriate object and complete the relationship.
Apart from the Answer #Wain (foreign key mapping) provided, it is also possible to implement a custom serialization (c.f. RKSerialization) and modify the objects before mapping takes place. However, the aforementioned method is superior to this (somehow ugly) solution.

RestKit Dynamic nested mapping

I see that the restkit document is quite nice and has variety of examples on object modelling. There is also an example of nested mapping but I find my scenario little bit different than this. RestKit documentation provides the example mapping of the nested attribute with the following json format.
Sample JSON structure from the RestKit Documentation :
{
"blake": {
"email": "blake#restkit.org",
"favorite_animal": "Monkey"
},
"sarah": {
"email": "sarah#restkit.org",
"favorite_animal": "Cat"
}
}
Suppose that my json is a bit different as this;
My JSON structure :
{
"id" : 1,
"author" : "RestKit",
"blake": {
"email": "blake#restkit.org",
"favorite_animal": "Monkey"
},
"sarah": {
"email": "sarah#restkit.org",
"favorite_animal": "Cat"
}
}
I created two different managedobject model with the following attributes and to many relations.
Two different entities for my structure Product and creator to map the above JSON object.
Product Creator
identifier <------------------- >> name
author email
favouriteAnimal
Now, my mapping would look like this for Product model would be;
This is how I map the Product entity,
[mapping mapKeyPath:"id" toAttribute:"identifier"];
[mapping mapKeyPath:"author" toAttribute: "author"];
But note here, mapping the nested dictionary attribute does not work for me.
// [mapping mapKeyOfNestedDictionaryToAttribute:#"creators"];
Now, in the authors class.
I could not figure out the usual way to map the above JSON structure.
If you have control over the web service, I would strongly recommend reorganizing your response data like this:
{
product:
{
id: 1,
author: 'RestKit',
creators: [
{
id: 1,
name: 'Blake',
email: '...',
favorite_animal: 'Monkey'
},
{
id: 2,
name: 'Sarah',
email: '...',
favorite_animal: 'Cat'
}
]
}
}
Following this structure, you'd be able to use RestKit's nested mapping features, and the relationship would be correctly reflected in the deserialized objects received by the object loader delegate. RestKit relies on naming and structure standards to simplify the code required to achieve the task. Your example deviates from key-value coding standards, so RK doesn't provide an easy way to interact with your data format.
If you don't have access or you can't change it, I think you'll need to map known key-value pairs with a mapping and perform the remaining assignments with a custom evaluator. You'd need to assume the unknown keys are actually name values for associated creators and their associated values contain the attribute hash for each. Using that, you'd then reconstruct each object manually.

RestKit: Map single object into existing array

I have the following JSON structure which i get from a RestService:
{
"customer": {
"id": "123456",
[more attributes ....]
"items": [
{
"id": "1234",
},
{
"id": "2345",
}
[more items...]
]
}
}
which i successfully map into Core Data using RestKit. From another RestService (which i can not change) i then get more details to one single item in the items array. the JSON answer looks like
{
"customer": {
"id: "123456",
"item": {
"id": "1234",
"name": "foo",
[other attributes...]
}
}
}
Now the question: How can i map the second answer, so that the single item is added to the items array (or updated if it is already in there)?
Thanks for any ideas!
If you already know how to map JSON to Core Data, all that's left is just fetch theobject you want to add your item attributes to(using id or something else) and then just set it,rewriting the old one,or adding new fields.That's just general approach
If you set the appropriate primaryKeyAttribute of the RKManagedObjectMapping object you should be able to perform the mapping as you want it to.
It would actually be easier to help you, if you would post some of your mapping code, but this is how I meant it to be
Create the mapping for your customer object, defining all possible attributes and declare the mappingObject.primaryKeyAttribute = #"id"
Execute the mapping with the first request (or first answer as you put it)
After the first mapping step is finished execute the second request
This should initially create the customer objects you want and then update them.