How to create model classes using JSONModel? - ios

I am trying to create Model Class by using JSONModel.My json Dictionary after using NSJSONSerialization looks like below.
{
apiStatus = {
message = SUCCESS;
success = 1;
};
boardingPoints = "<null>";
inventoryType = 0;
seats = (
{
ac = 0;
available = 1;
bookedBy = "<null>";
commission = "<null>";
fare = 1200;
},
{
ac = 0;
available = 1;
bookedBy = "<null>";
commission = "<null>";
fare = 1200;
},
);
}
The JSON looks like this:
{"boardingPoints":null,"inventoryType":0,"apiStatus":{"success":true,"message":"‌​SUCCESS"},"seats":[{"fare":1200,"commission":null,"bookedBy":null,"ac":false,"ava‌​ilable":true},{"fare":1200,"commission":null,"bookedBy":null,"ac":false,"availabl‌​e":true},]}
I have a model class like this :-
#interface Seat : JSONModel
#property (nonatomic,strong)NSString *ac;
#property (nonatomic,strong)NSString *available;
#property(nonatomic,strong)NSString *bookedBy;
#property(nonatomic,strong)NSString *comission;
#property(nonatomic)NSNumber * fare;
For mapping keys I have done like this:-
+(JSONKeyMapper*)keyMapper {
return [[JSONKeyMapper alloc] initWithDictionary:#{#"ac":#"ac",
#"available":#"available",
#"bookedBy":#"bookedBy",
#"commission":#"commission",
#"fare":#"fare", }];
}
However when I try to use this model I get the following error:
[JSONModel.m:252] Incoming data was invalid [Seat initWithDictionary:]. Keys missing: {(
bookedBy,
comission,
available,
ac,
fare,
)}
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'
I am using it like this:
//Using JSON Model.
NSError *jsonError;
seat = [[Seat alloc]initWithDictionary:jsonDictionary error:&jsonError];
jsonArray = #[seat.ac,seat.available,seat.bookedBy,seat.comission,seat.fare];
NSLog(#"JSON Model Array : %#", jsonArray);
How to use it correctly?

First of all, you don't need to override +(JSONKeyMapper*)keyMapper if your property names matches with the field names. Try putting the optional keyword for fields that can be null.
#interface Seat : JSONModel
#protocol Seat;
#property (nonatomic,strong)NSString *ac;
#property (nonatomic,strong)NSString<Optional> *available;
#property(nonatomic,strong)NSString<Optional> *bookedBy;
#property(nonatomic,strong)NSString *comission;
#property(nonatomic)NSNumber * fare;
#end
Taking this a step further, you can do cascading in your top class like this:
//Result.h
#import "Seat.h"
#import "APIStatus.h"
#interface Result: JSONModel
#property(nonatomic,strong) APIStatus *apiStatus;
#property(nonatomic,strong) NSString<Optional> *boardingPoints;
#property(nonatomic,strong) NSArray<Seat> *seats;
#property(nonatomic,strong) NSNumber *boardingPoints;
#end
//APIStatus.h
#interface APIStatus: JSONModel
#property(nonatomic,strong) NSString *message;
#property(nonatomic,strong) NSNumber *success;
#end
EDIT: This way you only need to init the Result model with JSONModel, all the intermediary classes will be created automatically. You may need to play around with the property types. JSONModel's github page offers good amount of explanations if you need a reference.

You need to correct your JSON string - remove the comma after the last entry of seats.
I think you may have to indicate you are working with nested models here i.e. seats are a nested model of your top level model which at this point is anonymous but really denoted by the top level { } braces.
Have a look at this - it describes how to set things up with nested models.
JSONModel tutorial covering nested models
Here is your corrected JSON string:
{
"boardingPoints": null,
"inventoryType": 0,
"apiStatus": {
"success": true,
"message": "‌​SUCCESS"
},
"seats": [
{
"fare": 1200,
"commission": null,
"bookedBy": null,
"ac": false,
"ava‌​ilable": true
},
{
"fare": 1200,
"commission": null,
"bookedBy": null,
"ac": false,
"availabl‌​e": true
}
]}

Related

Restkit fails to use appropriate Mapping

I am using Restkit 0.26.0 to map JSON with multiple layers and this key path causes trouble:
productColorImages: [
{
id: 10,
productId: "232",
color: "green",
url: "exampleURL.com"
},
{
id: 11,
productId: "232",
color: "red",
url: "exampleURL.com"
},
{
id: 12,
productId: "232",
color: "blue",
url: "exampleURL.com"
}
],
My mapping
RKObjectMapping *transferProductColorImageMapping = [RKObjectMapping mappingForClass:[TransferProductColorImage class]];
[transferProductColorImageMapping addAttributeMappingsFromDictionary:#{ #"identifier" : #"id",
#"URL" : #"URL",
#"productId" : #"productId",
#"color" : #"color"
[getProductPageMapping addRelationshipMappingWithSourceKeyPath:#"productColorImages" mapping:transferProductColorImageMapping];
The destination class
#interface TransferProductColorImage : NSObject
#property (nonatomic) NSNumber * identifier; // Mapps to id ;int 64
#property (nonatomic) NSString * URL;
#property (nonatomic) NSString * productId;
#property (nonatomic) NSString * color;
The problem is that the resulting Object is of the right class but the properties are not filled.
I searched in the logs and found this:
2016-05-30 17:42:24.007 thebrandsapp[20697:7047455] T restkit.object_mapping:RKMappingOperation.m:1173 Performing mapping operation: <RKMappingOperation 0x7f9e3b9c11e0> for 'TransferProductColorImage' object. Mapping values from object {
color = green;
id = 10;
productId = 232;
url = "exampleURL.com";
} to object <TransferProductColorImage: 0x7f9e3b846d60> with object mapping (null)
On the Model Object the productColorImages is a NSArray:
#property (nonatomic) NSArray <TransferProductColorImage *> * productColorImages;
This is odd because. How does Restkit know what mapping to use if the mapping is null. Is there any reason why the object mapping could be set to null?
Update: I found that the identifier and the color property were mapped correctly it is, the identifier and the url are not mapped correctly.
I changed the URL property to be lowercase: "url" and mapped it exactly like I did before and it worked. It seems like the the all-caps property name threw RestKit of.
I swooped identifier and id, now they map correctly as well.

Accessing a variable list through a class instance object

I currently develop an SDK and I want to know how to access a list of variables through a class instance object as follow:
MyClass * myObject = [[MyClass alloc] init];
[myObject changeShape : myObject.FORM_SQUARE];
[myObject changeShape : myObject.FORM_CIRCLE];
[myObject changeShape : myObject.FORM_RECTANGLE];
...
These variables ( three dozen ) are static and return just an integer to identify the form.
Do I have to set a #property for each variable or may be there are a more optimized way?
If they are not properties then you will have to implement a getter method to access them.
-(String*)getiVar{
return iVar;
}
Finally, the solution is to use NS_ENUM.
Like that:
typedef NS_ENUM(NSUInteger, shape) {
rectangle = 0,
triangle = 1,
square = 1,
...
};
-(void)changeShape:(shape)newShape;

Nested Select Query missing pointer and className -- Parse.com

When I do a query that requests just some selected fields of a pointer, the response that is received does not include the pointer type and class name.
Example :
PFQuery * query = [PFQuery queryWithClassName:#"ClassA"];
[query includeKey:#"column1"]; // This is a pointer with classname : ClassB
[query selectKeys:#[#"column1.subColumn1",#"column1.subColumn2"]];
The result is
"column1": {
"createdAt": "2015-10-29T19:46:17.167Z",
"subColumn1": "Some Value1",
"subColumn2": "Some Value2",
"objectId": "iCK9CpgKAh",
"updatedAt": "2015-11-16T14:30:11.312Z"
},
But, if I don't just select some fields, and include all subcolumns fields, it will work :
PFQuery * query = [PFQuery queryWithClassName:#"ClassA"];
[query includeKey:#"column1"]; // This will select all fields inside column1
Result :
"column1": {
"__type": "Pointer",
"className": "ClassB",
"createdAt": "2015-10-29T19:46:17.167Z",
"subColumn1": "Some Value1",
"subColumn2": "Some Value2",
"subColumn3": "Some Value3",
"objectId": "iCK9CpgKAh",
"updatedAt": "2015-11-16T14:30:11.312Z"
},
This create a problem when we use a PFSubclassing, because the pointer column is not treated as a pointer, but as a NSDictionary.
For example :
PFClassA and PFClassB are parse subclassess.
#interface PFClassB : PFObject<PFSubclassing>
#property (copy) NSString * subColumn1;
#property (copy) NSString * subColumn2;
#property (copy) NSString * subColumn2;
#end
#interface PFClassA : PFObject<PFSubclassing>
#property (strong) PFClassB * column1;
#end
When we do a query that just select 2 fields of the PFClassB like the above query,
the result of column1 will be a dictionary, and not a PFClassB.
Is there a workaround?
Thanks.

Mapping relationships from JSON array with Restkit

I will try to be as descriptive as possible with this issue...
Scenario
Let's say i have a NSManagedObject 'User'
#class Premise;
#interface User : NSManagedObject
#property (nonatomic, retain) NSNumber * identifier;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSSet *premises;
#end
#interface User (CoreDataGeneratedAccessors)
- (void)addPremisesObject:(Premise *)value;
- (void)removePremisesObject:(Premise *)value;
- (void)addPremises:(NSSet *)values;
- (void)removePremises:(NSSet *)values;
#end
And i also have a NSManagedObject 'Premise'
#class User;
#interface Premise : NSManagedObject
#property (nonatomic, retain) NSNumber * identifier;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) User *user;
#end
Based on it, i am creating a relationship route to map a JSON array of 'Premise' to the 'premises' attribute on the 'User' object.
Here's the route:
let getPremisesRoute = RKRoute(relationshipName: "premises",
objectClass: User.self,
pathPattern: "user/:identifier/premises",
method: .GET)
Here's the JSON response (/user/1/premises):
[
{
"id": 35,
"name": "Icaraí"
},
{
"id": 32,
"name": "Remanso"
}
]
Here's the response descriptor:
let getPremisesResponseDescriptor = RKResponseDescriptor(mapping: premiseMapping, method: .GET, pathPattern: "user/:identifier/premises", keyPath: nil, statusCodes: RKStatusCodeIndexSetForClass(.Successful))
And here are the respective mappings of 'User' and 'Premise'
let userMapping = RKEntityMapping(forEntityForName: "User", inManagedObjectStore: moc)
userMapping.addAttributeMappingsFromDictionary(["id":"identifier", "name":"name"])
userMapping.identificationAttributes = ["identifier"]
userMapping.addPropertyMapping(RKRelationshipMapping(fromKeyPath: nil, toKeyPath: "premises", withMapping: premiseMapping))
let premiseMapping = RKEntityMapping(forEntityForName: "Premise", inManagedObjectStore: moc)
premiseMapping.addAttributeMappingsFromDictionary(["id":"identifier", "name":"name"])
premiseMapping.identificationAttributes = ["identifier"]
Now to my problem
Apparently, Restkit is getting a little bit confused during the mapping process. Here's the database after the request:
User Table:
Premise Table:
Note the the relationship is not being created between the entities.
Now, if I change the response descriptor's mapping from premise to user mapping, the database changes to this:
Users Table:
Premise Table:
I'm really confused on what's going on and I've tried a lot of solutions with no success.
Is the JSON response out of pattern or am I doing something wrong? The JSON response seems to be on a common pattern, with a nil key path.
You're approaching the mapping incorrectly, or at least your mappings are wrong for what you're doing. Consider that the response is a user, but only the premises for a user, instead of considering it as a simple list of premises as you are now. Then you map to a user and insert the premises. Something like:
RKResponseDescriptor(mapping: userMapping, method: .GET, pathPattern: "user/:identifier/premises", keyPath: nil, statusCodes: RKStatusCodeIndexSetForClass(.Successful))
And here are the respective mappings of 'User' and 'Premise'
let userMapping = RKEntityMapping(forEntityForName: "User", inManagedObjectStore: moc)
userMapping.addAttributeMappingsFromDictionary(["#metadata.routing.parameters.idEntities":"identifier"])
userMapping.identificationAttributes = ["identifier"]
userMapping.addPropertyMapping(RKRelationshipMapping(fromKeyPath: nil, toKeyPath: "premises", withMapping: premiseMapping))
let premiseMapping = RKEntityMapping(forEntityForName: "Premise", inManagedObjectStore: moc)
premiseMapping.addAttributeMappingsFromDictionary(["id":"identifier", "name":"name"])
premiseMapping.identificationAttributes = ["identifier"]
You don't have a user name in the response so you can't map it, and the user id is actually in the request so you need to use metadata to extract it.
Ok, I found a possible solution based on #Wain's solution using foreign keys.
I added a new property 'userID' to the 'Premise' entity and mapped it to the identifier on the URL using metadata
let premiseMapping = RKEntityMapping(forEntityForName: "Premise", inManagedObjectStore: moc)
premiseMapping.addAttributeMappingsFromDictionary(["id":"identifier", "name":"name", "#metadata.routing.parameters.identifier":"userID"])
Then I added a relationship connection to the 'premiseMapping'
premiseMapping.addConnectionForRelationship("user", connectedBy: ["userID":"identifier"])
If anyone has a more elegant solution please share with us.

Parsing JSON in iOS, inner dictionaries

I am new to JSON and iOS, I have some json data that I want to load into an array,
This is the json data
{
"items": [
{
"name": "abc",
"description": "cheeseburger",
},
{
"name": "def",
"description": "ostrichburger",
},
{
"name": "zxc",
"description": "sh1tjustgotreal",
},
{
"name": "scfs",
"description": "mylifeforaiur",
}
]
}
Now I keep on getting dictionaries with dictionaries? Why is that?
On another note, If I can modify the structure of this json cos I really just want to access the inner nodes ( abc, def ) what would I change in it to make it simpler for me and others to use it? Can I get rid of the "items" node?
When the data on the sender's end is structured as a collection of named fields, you get back a dictionary for each group of named fields. In this particular case, it looks like the outermost object has one field, which is a collection (i.e. a list or an array) of objects that each have a name and description. In other words, the sender sends you something like this:
#interface Item
#property (readwrite, copy) NSString* name;
#property (readwrite, copy) NSString* description;
#end
#interface MyObject
// This array contains objects of type Item
#property (readwrite) NSArray *items;
#end

Resources