RestKit: Composing Relationships with the Nil Key Path - objective-c

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.

Related

JSON schema recursion doesnt seem to properly validate

I'm going through the docs to try and figure out how loops work so I can validate every object of an array of objects match the schema.
It seems like recursion is what I want but the example given doesn't work: https://json-schema.org/understanding-json-schema/structuring.html
I'm trying to validate that example but its always "valid". I tried changing all the field names in the JSON and it doesn't matter:
Not sure what's happening. For this example how would I validate every child matches the person schema (without statically writing out each one in the schema).
For example, I want to valid this JSON. there could be any number of objects under toplevel and any number of objects under "objectsList". I want to make sure every object under "objectsList" has the right field names and types (again without hard coding the entire thing in the schema):
{
"toplevel": {
"objectOne": {
"objectsList": [
{
"field1": 1231,
"field2": "sekfjlskjflsdf",
"field3": ["ssss","eeee"],
},
{
"field1": 11,
"field2": "sef",
"field3": ["eeee","qqqq"],
},
{
"field1": 1231,
"field2": "wwwww",
"field3": ["sisjflkssss","esdfsdeee"],
},
]
},
"objectTwo": {
"objectsList": [
{
"field1": 99999,
"field2": "yuyuyuyuyu",
"field3": ["ssssuuu","eeeeeee"],
},
{
"field1": 221,
"field2": "vesdlkfjssef",
"field3": ["ewerweeee","ddddq"],
},
]
},
}
}
What's wrong?
The problem here is not the recursion – your schema looks good.
The underlying issue is the same as here: https://stackoverflow.com/a/61038256/5127499
JSON Schema is designed for extensibility. That means it allows any kind of additional properties to be added as long as they are not conflicting with the known/expected keywords.
Solution
The solution here is to add "additionalProperties": false in your "person" (from the screenshot) and top-level schema to prevent those incorrect objects to be accepted. Same goes for your second example: in any definitions of "type": "object" you'd have to add "additionalProperties": false if you don't want to allow these extraneous properties to be defined.
Alternatively, you can declare your expected properties as required to ensure that at least those are present.
Why?
As per json-schema.org/understanding-json-schema (emphasis mine):
The additionalProperties keyword is used to control the handling of extra stuff, that is, properties whose names are not listed in the properties keyword. By default any additional properties are allowed.
The additionalProperties keyword may be either a boolean or an object. If additionalProperties is a boolean and set to false, no additional properties will be allowed.
To address the screenshot you posted and why the instance passes:
The schema is looking to find a person property, but that property doesn't exist.
The schema does not declare that person is required.
The schema does not declare requirements on undefined properties, so it will always accept the personsdfsd property with whatever value is in it, without checking it further.
So in short, your JSON data is bad and your schema doesn't have any protections against that.
Other than that, your schema looks good. It should validate that items in the children property match the person definition's subschema.

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 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.

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

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;
}