RestKit 0.2: flattening the hierarchy in JSON - ios

Maybe there's a similar question here, but I could not find it. So I have the RestKit 0.2pre4 and want to map such JSON ...
{
"location": {
"position": {
"latitude": 53.9675028,
"longitude": 10.1795594
}
},
"id": "da3224f2-5919-42f2-9dd8-9171088f4ad7",
"name": "Something even more beautiful",
}
... to such an object:
#interface FavoritePlace : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * placeId;
#property (assign, nonatomic) double latitude;
#property (assign, nonatomic) double longitude;
#end
As you can see, the point is that I don't want to create a relationship and store "Location" object in database separately - it just doesn't make sense. Therefore I want to assign those nested things (location.position.latitude and location.position.longitude) to particular properties of object itself. How to achieve that?

Apparently, I did a wild ass guess and it worked! Here's the mapping which does the trick:
[placeMapping addAttributeMappingsFromDictionary:#{
#"id": #"placeId",
#"name": #"name",
#"location.position.latitude": #"latitude",
#"location.position.longitude": #"longitude"
}];
Have not found this in documentation, but maybe it's just me... Anyway, if something logical, but undocumented, works, then framework should be good. :) RestKit FTW!

You can pass the string containig the json string to nsdata with
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
Then, you will be able to parse de json data with the iOS API with
NSError *error;
NSDictionary *json = jsonData ? [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error] : nil;
Now, examine the generated NSDictionary (with NSlog for example), fill your 'FavoritePlace' object and persist it.
Hope helps!

Related

Better JSON parsing implementation in Objective-c and best practices?

I am receiving below JSON response for saving user preferences in core data.
preferences={
1={
children=({
3=Samsung;4=Nokia;
});id=1;name=Mobiles;
};2={
children=({
5="Samsung Curve TV";
});id=2;name=Electronics;
};
};
Here is my code snippet which is working fine. But I think this is much verbose code.
NSLog(#"Preferences: %#", [response objectForKey:#"preferences"]);
for (NSDictionary *dic in [response objectForKey:#"preferences"]) {
NSLog(#"ID: %#", [[[response objectForKey:#"preferences"] objectForKey:dic] objectForKey:#"id"]);
NSLog(#"NAME: %#", [[[response objectForKey:#"preferences"] objectForKey:dic] objectForKey:#"name"]);
NSLog(#"Children DIC: %#", [[[[[response objectForKey:#"preferences"]
objectForKey:dic] objectForKey:#"children"] objectAtIndex:0] objectForKey:#"3"]);
for (NSDictionary *childDic in [[[[response objectForKey:#"preferences"]
objectForKey:dic] objectForKey:#"children"] objectAtIndex:0]) {
NSLog(#"Child Name: %#", [[[[[response objectForKey:#"preferences"]
objectForKey:dic] objectForKey:#"children"] objectAtIndex:0] objectForKey:childDic]);
}
}
I have 3 questions.
How can I improve my code snippet? is there any shorter way to implement this?
Is this JSON response is good enough for mobile end parsing? is it good JSON format? Is there any JSON response format we should follow as Mobile developers in terms of using Core data (which simply reduces DB implementation as best practice)?
How do I construct a JSON string like this again from Objective-c?
Ok so, (Sorry for NOT deep analysis) first of all I'd modify your JSON not to contain dictionary for dynamic elements (samsung has key 3 and so on, it should be array, make sense?)
I come up with imho better JSON structure:
{
"preferences":[
{
"items":[
{
"id" : "3",
"name" : "Samsung"
},
{
"id" : "3",
"name" : "Nokia"
}
],
"id":"1",
"name":"Mobiles"
},
{
"items":[
{
"id" : "3",
"name" : "Nokia"
}
],
"id":"2",
"name":"Electronics"
}
]
}
It's really easy now to map it to objects with JSONModel and only one thing you should care about is mapping keys.
NSString *JSONString = #" { \"preferences\":[ { \"items\":[ { \"id\" : \"3\", \"name\" : \"Samsung\" }, { \"id\" : \"3\", \"name\" : \"Nokia\" } ], \"id\":\"1\", \"name\":\"Mobiles\" }, { \"items\":[ { \"id\" : \"3\", \"name\" : \"Nokia\" } ], \"id\":\"2\", \"name\":\"Electronics\" } ] }";
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:[JSONString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
NSError *mapError;
GlobalPreferences *globalPreferences = [[GlobalPreferences alloc] initWithDictionary:dictionary error:&mapError];
if (mapError) {
NSLog(#"Something went wrong with mapping model %#", mapError);
}
NSLog(#"Your mapped model: %#", globalPreferences);
Models:
Kinda GlobalPreference, root model, in case if u decide to add something extra
#import <JSONModel/JSONModel.h>
#import "Preference.h"
#interface GlobalPreferences : JSONModel
#property (nonatomic, strong) NSArray<Preference> *preferences; // using protocol here you specifying to which model data should be mapped
#property (nonatomic, strong) NSArray<Optional> *somethingElse; // some other settings may be here
#end
#import "GlobalPreferences.h"
#implementation GlobalPreferences
#end
Preference
#import <JSONModel/JSONModel.h>
#import "PreferenceItem.h"
#protocol Preference <NSObject>
#end
#interface Preference : JSONModel
#property (nonatomic, strong) NSNumber *ID; // required
#property (nonatomic, strong) NSString *name; // required
#property (nonatomic, strong) NSArray<PreferenceItem> *items;
#end
#import "Preference.h"
#implementation Preference
#pragma mark - JSONModel
+ (JSONKeyMapper *)keyMapper {
return [[JSONKeyMapper alloc] initWithDictionary:#{
#"id": #"ID",
}];
}
#end
PreferenceItem
#import <JSONModel/JSONModel.h>
// This protocol just to let JSONModel know to which model needs to be parsed data in case if it's an array/dictionary
#protocol PreferenceItem <NSObject>
#end
#interface PreferenceItem : JSONModel
#property (nonatomic, strong) NSNumber *ID;
#property (nonatomic, strong) NSString *name;
#end
#import "PreferenceItem.h"
#implementation PreferenceItem
#pragma mark - JSONModel
+ (JSONKeyMapper *)keyMapper {
return [[JSONKeyMapper alloc] initWithDictionary:#{
#"id": #"ID",
}];
}
#end
Should be ok for coreData.
Maybe for you case all this is not necessary, but there are lot of things you need to care when u parsing/mapping network response such es data types, missing keys, error handling and so on. If u map it manually - you risk to have broken app some day.
I know you asked best practices for Objective-C, but I would suggest switching to Swift and using SwiftyJSON. With Swift the code is much more readable than with Objective-C.
let prefs = json["preferences"]
let userName = prefs["id"].stringValue
let name = prefs["name"].stringValue
let child = prefs["children"].arrayValue[0].arrayValue[3]
https://github.com/SwiftyJSON/SwiftyJSON
You can easily build your own JSON structures and send them with Alamofire:
let parameters: [String : AnyObject] = [
"preferences": [
"id": "123",
"name": "John",
"children": [
["3": "Three"]
]
]
]
let urlReq = NSMutableURLRequest(url)
urlReq.HTTPMethod = .POST
let req = Alamofire.ParameterEncoding.JSON.encode(urlReq, parameters: parameters).0
let request = Alamofire.request(req)
https://github.com/Alamofire/Alamofire

JSONModel NSArray ConvertOnDemand

How can I create a ConvertOnDemand 'NSArray *' using the JSONModel library from the following JSON response: -
[
{"id": 1, "name": "jim"},
{"id": 2, "name": "lovy"}
]
Please check here if you want to know more JSONModel ConvertOnDemand (https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=jsonmodel+convertonDemand).
If you're open to using the native Foundation framework instead, you could do something like this
NSArray *arr = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&err];
If your JSON is in the form of a string, just convert to NSData before calling the above
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
This solution will give you an NSArray of NSDictionaries
May be BWJSONMatcher is what you are looking for.
Declare your data model that matches your json string:
#interface YourDataModel : NSObject
#property (nonatomic, assign) NSInteger id;
#property (nonatomic, strong) NSString *name;
#end
Then your can use BWJSONMatcher to convert the json string to an NSArray which can be directly used in your ViewControllers.
NSArray *jsonArray = [BWJSONMatcher matchJSON:jsonString withClass:[YourDataModel class]];
ConvertOnDemand is optional for your model class, if your entity class is Person class, declare the array property like this,
#property (nonatomic, strong) NSArray<Person, ConvertOnDemand> *persons;
#property (nonatomic, strong) NSArray<Person> *persons;
For the former one, the persons array type is JSONModelArray[Person], it just means all the person object has conformed Person protocol. For the later one, the type is NSArray with unconfirmed objects, but they are really Person type.

do i need to create mapping for all objects of json that i get?

I have questions about rest kit.
do i need to create mapping for all objects of json that i get ? or i can provide mapping only fore some of this objects and restkit will work properly even if I did not create mapping for some objects that I am loading from internet?
for example there are this classes
#interface RKAUser : NSObject
#property (strong, nonatomic) NSString* nameString;
#property (strong, nonatomic) RKCard* card;
#end
#interface RKACard : NSObject
#property (strong, nonatomic) NSString* numberString;
#end
+ (RKMapping *)userMapping {
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKAUser
class]];
[mapping addAttributeMappingsFromArray:#[#"nameString"]];
return mapping;
}
I have json where is information for about 20 users
in this json there are all user information including nameString and his cards numberString.
but I create mapping only for nameString and did not create mapping for card
question for this example will be would this code work or it will crash maybe because I did not provide all mapping
another question
can i use restkit for mapping any json information or not?
does information should be definitely from some kind of rest server or rest kit can map json information from any server? will it work if information is not from server?
You only need to implement properties that you used when creating your mappings.
JSON doesn't need to come from the server. You can take any JSON document, then parse it yourself and use RKMapperOperation with the result.
RKMapperOperation *operation = [[RKMapperOperation alloc] initWithRepresentation:parsedJSON mappingsDictionary:yourMappingsDictionary];
NSError *error = nil;
[operation execute:&error];
if (!error) {
NSArray *result = mapper.mappingResult.array;
}

Posting an array of objects not working with Restkit [duplicate]

This question already has an answer here:
Restkit request not sending parameters
(1 answer)
Closed 8 years ago.
I have the following two entities
#interface MEContactInfo : NSObject
#property (nonatomic,strong) NSString* phone ;
#property (nonatomic,strong) NSString* email;
#end
#interface MEContact : NSObject
#property (nonatomic,strong) NSString* _id ;
#property (nonatomic,strong) NSString* lastName;
#property (nonatomic,strong) NSString* firstName;
#property (nonatomic,strong) NSString* data ;
#property (nonatomic,strong) NSMutableArray* contactInfos ;
#end
The second entity contact contains the array of contact infos. Now I want to post this to my server but I am not able to do so. My mappings are as following:
RKObjectMapping* contactMapping = [RKObjectMapping mappingForClass:[MEContact class]];
[contactMapping addAttributeMappingsFromArray:#[#"_id",#"lastName",#"firstName",#"data"]];
RKObjectMapping* contactInfosMapping = [RKObjectMapping mappingForClass:[MEContactInfo class]];
[contactInfosMapping addAttributeMappingsFromArray:#[#"email",#"phone"]];
[contactMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"contactInfos" toKeyPath:#"contactInfos" withMapping:contactInfosMapping]];
My request descriptor is as following:
requestDescriptor = [RKRequestDescriptor
requestDescriptorWithMapping:[contactMapping inverseMapping]
objectClass: [MEContact class]
rootKeyPath:nil method:RKRequestMethodAny];
Now when I post something like this:
{ firstName:”abc”,
lastName:”xyz”,
contactInfos: [{
email:”test#test.com”,
phone:”9999999999”
}]
}
I receive
{
firstName:”abc”,
lastName:”xyz”,
contactInfos: [ ”test#test.com”,”9999999999”]
}
If I have multiple entries in the contactInfos array, they all are appended to the contactInfos array I receive on the server side. Basically the contactInfo object is flattening in an array. Can you please let me know how I can fix this.
I got it to work. Everything above was correct. The problem was that data was not going as JSON to the server. The solution was that I had to set request serialization MimeType which can be done by doing this
[objectManger setRequestSerializationMIMEType:RKMIMETypeJSON];
Thanks

How to create objects from JSON NSDictionary

I have a PHP Webservice that returns a JSON string with this format:
[{"latitud":"37.995914","longitud":"-1.139705","nombre":"Miguel de
Unamuno"},{"latitud":"37.995433","longitud":"-1.140143","nombre":"Calle
Pina"},{"latitud":"37.99499","longitud":"-1.140361","nombre":"Calle
Moncayo"},{"latitud":"37.993918","longitud":"-1.139392","nombre":"Calle
Moncayo2"},{"latitud":"37.994588","longitud":"-1.138543","nombre":"Calle
Salvador de Madriaga"}]
In my project, I have a custom class with the next structure:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface PCoordenada : NSObject
#property (nonatomic) CLLocationCoordinate2D *punto;
#property (nonatomic,strong) NSString *nombre;
#end
Then, I´m using other class for the main app:
#import <UIKit/UIKit.h>
#import "PCoordenada.h"
#interface TestViewController : UIViewController
#property (nonatomic,strong) NSData * HTTPResponse;
#property (nonatomic,strong) NSDictionary * dic;
#property (nonatomic,strong) NSMutableArray *arrayCoord;
#property (nonatomic,strong) PCoordenada *coor;
-(IBAction)GetDataFrom:(id)sender;
#end
I wonder how I can make a array of PCoordenada's objects that contain the info of JSON string.
Anyone could help me?
Thanks in advance :)
Do this:
NSData *theData = [NSData dataWithContentsOfURL:[NSURL URLWithString:YOUR_URL]];
NSArray *arrRequests = [NSJSONSerialization JSONObjectWithData:theData options:NSJSONReadingMutableContainers error:nil];
which will put the JSON into an NSArray of objects. Each of these objects is an NSDictionary. So then you just need to loop through the NSArray to get out the NSDictionary of each.
//now let's loop through the array and generate the necessary annotation views
for (int i = 0; i<= arrRequests.count - 1; i++) {
//now let's dig out each and every json object
NSDictionary *dict = [arrRequests objectAtIndex:i];}
Each NSDictionary that you get from the loop holds the JSON properties as a key in the NSDictionary:
NSString *address = [NSString stringWithString:[dict objectForKey:#"Address"]];
It's also a good practice to use multithreading when reading JSON for better performance.
This article has a very simple to follow how-to. I recommend a read.

Resources