Parsing JSON in iOS, inner dictionaries - ios

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

Related

Mapping Firebase lists to objects with Mantle

I'm looking for an effective way to map Firebase lists to custom Objective-C objects with Mantle. Firebase doesn't really have a concept of arrays, so every item in a list has an explicit id. A list in Firebase of type event appears something like this:
"events": {
"id0": {
"name": "Event 1",
"date": "2017-03-28T08:00:00+00:00",
"venue": "Venue 1"
},
"id1": {
"name": "Event 2",
"date": "2017-03-29T08:00:00+00:00",
"venue": "Venue 2"
},
...
"id5": {
"name": "Event 6",
"date": "2017-04-05T08:00:00+00:00",
"venue": "Venue 6"
}
}
In Objective-C terms, this translates perfectly well into an NSDictionary, but it's easier to work with an NSArray in my UITableViewController. At the moment I'm using NSDictionary for each event, and simply moving the event's key inside the Dictionary and creating an array of dictionaries as follows (feel free to comment on whether this is a good or bad idea):
{
"eventId": "id0",
"name": "Event 1",
"date": "2017-03-28T08:00:00+00:00",
"venue": "Venue 1"
},
{
"eventId": "id1",
"name": "Event 2",
"date": "2017-03-29T08:00:00+00:00",
"venue": "Venue 2"
}
...
My idea is to then create an Event object:
#interface Event: NSObject <MLTModel>
#property (nonatomic, strong) NSString *eventId;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSDate *date;
#property (nonatomic, strong) NSString *venue;
#end
So my question is this: is there a way to map this Firebase list to a custom Event object with Mantle so that it can be easily used for UITableViews in iOS? Specifically, how does one map the key (id0, id1 etc) of the Firebase event to be a property of the custom Event object, and back again? Or would it be better to move the key "manually" and then hand over to Mantle?

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.

Filter an array with predicate in iOS

I have an array of JSONModel objects. The JSONModel object looks like this
#interface ProductModel : JSONModel
#property(nonatomic, strong) NSString *name;
#property(nonatomic, strong) NSString *price;
#property(nonatomic, strong) NSArray *productCategories;
#end
Inside the productCategory model I have this
#interface ProductCategoryModel : JSONModel
#property (nonatomic,assign) NSString *id;
#end
The json looks like this
{
"productCount":"129",
"products": [
{
"name": "Art attack",
"price": "$19.99",
"prouctCategories":[
{ "id": "50" }
{ "id": "10" }
]
}
I have to filter the array checking the id property of a productCategory in the array productCategories.One product can have multiple categories. I need to get for example the list of products with the id : 10.
I want to get that list using filteredArrayUsingPredicate method of an NSArray but I cannot find the right predicate format to get this. Is it possible to do that using predicate.
I tried this predicate but I didn't get anything
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%# == '%#"', #"id", #"50"];
NSPredicate documentation is pretty clear about building this kind of queries. You can use ANY clause to apply filter on many-to-many relationships to get the products whose any of the category's ID is 10.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(ANY productCategories.id == '10')"];
NSArray *filteredProducts = [products filteredArrayUsingPredicate:predicate];
To simplify the problem change the json to look like this:
{
"productCount":"129",
"products": [
{
"name": "Art attack",
"price": "$19.99",
"productCategories":["50","10"]
}
So you can use such predicate:
[NSPredicate predicateWithFormat:#"productCategories CONTAINS '10')"]
You can use NSPredicate as your json is like
{
"productCount":"129",
"products": [
{
"name": "Art attack",
"price": "$19.99",
"prouctCategories":[
{ "id": "50" }
{ "id": "10" }
]
}
then you may check it with this:
NSArray *arrFiltered = [arrProducts filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(self.prouctCategories.id == '10')"]];
Or you may also change your json format to make things more easier like
{
"productCount":"129",
"products": [
{
"name": "Art attack",
"price": "$19.99",
"prouctCategories":[
{ "id": 50,10,20 }
]
}
So you may filtered id like this
NSArray *arrFiltered = [arrProducts filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(self.prouctCategories.%K CONTAINS '10')", 'id']];
Hope it will help you. :)

How to create model classes using JSONModel?

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

RKObjectMapping connections

I have neхt response:
{
"orders": [
{
"date": "2013-11-18T13:00:39.000Z",
"state": "new",
"id": 11,
"order_item_ids": [
27,
28
]
}
],
"order_items": [
{
"count": 2,
"id": 27,
"item_id": 1,
"order_id": 11
},
{
"count": 1,
"id": 28,
"item_id": 2,
"order_id": 11
}
]
}
And next object I want to map it:
#interface Order : NSObject
#property (nonatomic, strong) NSString* state;
#property (nonatomic, strong) NSMutableArray* orderItems;
#property (nonatomic, strong) NSDate* visitDate;
#end
orderItems should be array of:
#interface OrderItem : NSObject
#property (nonatomic) NSUInteger ID;
#property (nonatomic) NSUInteger itemID;
#property (nonatomic) NSUInteger count;
#end
In case of RKEntityMapping I should use addConnectionForRelationship to map order_item_ids to orderItems array via order_items response. But what should I do to do to connect it in case of RKObjectMapping? Off course I can map both orders and orderItems with separate response descriptors and than parse it, but I want to make RestKit do it for me. Another idea is to use CoreData and RKEntityMapping but I'm not sure that I want in this case, it will be overkill in this case.
You can't have it done automatically with RKObjectMapping because you can't index into both arrays in any way. The entity mapping only works because you can use foreign key mapping after both mappings have been performed. You can duplicate this by using multiple response descriptors and mappings and then combining the contents of the mapping result.

Resources