Objective C + RESTKit: Problems with getting Objects - iphone

I want to use RestKit for a Restful Webservice to communicate between an LAMP-environment and an iOS-App.
Therefore I made a first test to check out if it all works out like described in the tutorials available and on the RestKit page.
Right now the code looks like this:
RKObjectManager* objectManager = [RKObjectManager managerWithBaseURLString:#"http://localhost/resttest"];
objectManager.client.requestQueue.showsNetworkActivityIndicatorWhenBusy = YES;
#ifdef RESTKIT_GENERATE_SEED_DB
NSString *seedDatabaseName = nil;
NSString *databaseName = RKDefaultSeedDatabaseFileName;
#else
NSString *seedDatabaseName = RKDefaultSeedDatabaseFileName;
NSString *databaseName = #"CoreData.sqlite";
#endif
objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:databaseName usingSeedDatabaseName:seedDatabaseName managedObjectModel:nil delegate:self];
// Map Person Object
RKManagedObjectMapping *personMapping = [RKManagedObjectMapping mappingForClass:[Person class] inManagedObjectStore:objectManager.objectStore];
//RKManagedObjectMapping *personMapping = [RKManagedObjectMapping mappingForEntityWithName:#"Person" inManagedObjectStore:[RKObjectManager sharedManager].objectStore];
[personMapping mapKeyPath:#"id" toAttribute:#"person_id"];
[personMapping mapKeyPath:#"firstname" toAttribute:#"person_firstname"];
[personMapping mapKeyPath:#"lastname" toAttribute:#"person_lastname"];
[personMapping mapKeyPath:#"gender" toAttribute:#"person_gender"];
personMapping.primaryKeyAttribute = #"id";
// Register our mappings with the provider;
[objectManager.mappingProvider setMapping:personMapping forKeyPath:#"/person"];
objectManager.acceptMIMEType = RKMIMETypeJSON;
#ifdef RESTKIT_GENERATE_SEED_DB
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelInfo);
RKLogConfigureByName("RestKit/CoreData", RKLogLevelTrace);
#endif
[objectManager loadObjectsAtResourcePath:#"/person" delegate:self];
This is how my Managed Object looks like:
#interface Person : NSManagedObject
#property (nonatomic, retain) NSString * person_firstname;
#property (nonatomic, retain) NSString * person_lastname;
#property (nonatomic, retain) NSString * person_gender;
#property (nonatomic, retain) NSNumber * person_id;
#end
And this is the dummy-code on server side which only returns some json example string:
<?php
header('Content-type: application/json');
if($_SERVER['REQUEST_METHOD'] == 'GET')
{
$json_string = '{ "persons": [
{ "id": "1",
"firstname": "hans",
"lastname": "maier",
"genre": "m"
},
{ "id": "2",
"firstname": "michael",
"lastname": "schmidt",
"genre": "w"
}]
}';
echo $json_string;
}
?>
The URI to get the returned json-String is http://localhost/resttest/person(/index.php).
My problems are:
I always get this Error when i don't define a resource path for '':
E restkit.network:RKObjectLoader.m:216 Encountered errors during mapping: Could not find an object mapping for keyPath: ''
and I don't know why this happens. No other example I tried has this problem, but I don't have a clue what I'm doing wrong.
When it works (I have to change the Base URL to http://localhost/resttest/person), I get an empty object instead of an error.
I would appreciate any help on this topic, because right now i don't know what I'm doing wrong.
Thank you!
Edit 03.05.2012:
Here is what i get in the logs:
2012-05-03 12:52:05.067 RESTKitCoreDataTest[5139:13807] D restkit.object_mapping:RKObjectMapper.m:320 Performing object mapping sourceObject: {
persons = (
{
firstname = hans;
gender = m;
id = 1;
lastname = maier;
},
{
firstname = michael;
gender = w;
id = 2;
lastname = schmidt;
}
);
}
and targetObject: (null)
2012-05-03 12:52:05.067 RESTKitCoreDataTest[5139:13807] T restkit.object_mapping:RKObjectMapper.m:278 Examining keyPath '' for mappable content...
2012-05-03 12:52:05.067 RESTKitCoreDataTest[5139:13807] D restkit.object_mapping:RKObjectMapper.m:264 Found mappable data at keyPath '': {
persons = (
{
firstname = hans;
gender = m;
id = 1;
lastname = maier;
},
{
firstname = michael;
gender = w;
id = 2;
lastname = schmidt;
}
);
}
2012-05-03 12:52:05.073 RESTKitCoreDataTest[5139:13807] D restkit.object_mapping:RKObjectMapper.m:210 Asked to map source object {
persons = (
{
firstname = hans;
gender = m;
id = 1;
lastname = maier;
},
{
firstname = michael;
gender = w;
id = 2;
lastname = schmidt;
}
);
} with mapping <RKManagedObjectMapping:0x811eb60 objectClass=Person keyPath mappings => (
"RKObjectKeyPathMapping: id => person_id",
"RKObjectKeyPathMapping: firstname => person_firstname",
"RKObjectKeyPathMapping: lastname => person_lastname",
"RKObjectKeyPathMapping: gender => person_gender"
)>
2012-05-03 12:52:05.074 RESTKitCoreDataTest[5139:13807] D restkit.object_mapping:RKObjectMappingOperation.m:617 Starting mapping operation...
2012-05-03 12:52:05.074 RESTKitCoreDataTest[5139:13807] T restkit.object_mapping:RKObjectMappingOperation.m:618 Performing mapping operation: RKObjectMappingOperation for 'Person' object. Mapping values from object {
persons = (
{
firstname = hans;
gender = m;
id = 1;
lastname = maier;
},
{
firstname = michael;
gender = w;
id = 2;
lastname = schmidt;
}
);
} to object <Person: 0x6e295d0> (entity: Person; id: 0x6e29610 <x-coredata:///Person/t99EFF6DC-EBDA-405C-8AFF-D082E4A8542F2> ; data: {
"person_firstname" = nil;
"person_gender" = nil;
"person_id" = 0;
"person_lastname" = nil;
}) with object mapping <RKManagedObjectMapping:0x811eb60 objectClass=Person keyPath mappings => (
"RKObjectKeyPathMapping: id => person_id",
"RKObjectKeyPathMapping: firstname => person_firstname",
"RKObjectKeyPathMapping: lastname => person_lastname",
"RKObjectKeyPathMapping: gender => person_gender"
)>
2012-05-03 12:52:05.074 RESTKitCoreDataTest[5139:13807] T restkit.object_mapping:RKObjectMappingOperation.m:388 Did not find mappable attribute value keyPath 'id'
2012-05-03 12:52:05.074 RESTKitCoreDataTest[5139:13807] T restkit.object_mapping:RKObjectMappingOperation.m:388 Did not find mappable attribute value keyPath 'firstname'
2012-05-03 12:52:05.075 RESTKitCoreDataTest[5139:13807] T restkit.object_mapping:RKObjectMappingOperation.m:388 Did not find mappable attribute value keyPath 'lastname'
2012-05-03 12:52:05.075 RESTKitCoreDataTest[5139:13807] T restkit.object_mapping:RKObjectMappingOperation.m:388 Did not find mappable attribute value keyPath 'gender'
2012-05-03 12:52:05.075 RESTKitCoreDataTest[5139:13807] D restkit.object_mapping:RKObjectMappingOperation.m:638 Mapping operation did not find any mappable content
2012-05-03 12:52:05.075 RESTKitCoreDataTest[5139:13807] T restkit.core_data:RKManagedObjectMappingOperation.m:98 relationshipsAndPrimaryKeyAttributes: {
}
2012-05-03 12:52:05.075 RESTKitCoreDataTest[5139:13807] D restkit.object_mapping:RKObjectMapper.m:351 The following operations are in the queue: (
)
2012-05-03 12:52:05.075 RESTKitCoreDataTest[5139:13807] D restkit.object_mapping:RKObjectMapper.m:366 Finished performing object mapping. Results: {
}
2012-05-03 12:52:05.080 RESTKitCoreDataTest[5139:fb03] Loaded persons: (
)

Possibility 1:
It seems that your setting the mapping to the wrong keypath.
Try
// Register our mappings with the provider;
[objectManager.mappingProvider setMapping:personMapping forKeyPath:#"persons"];
and check if it works.
Possibility 2:
Specify the root path of your JSON in your mapping.
Try
// Map Person Object
RKManagedObjectMapping *personMapping = [RKManagedObjectMapping mappingForClass:[Person class] inManagedObjectStore:objectManager.objectStore];
[personMapping mapKeyPath:#"id" toAttribute:#"person_id"];
[personMapping mapKeyPath:#"firstname" toAttribute:#"person_firstname"];
[personMapping mapKeyPath:#"lastname" toAttribute:#"person_lastname"];
[personMapping mapKeyPath:#"gender" toAttribute:#"person_gender"];
personMapping.rootKeyPath = #"persons";
personMapping.primaryKeyAttribute = #"id";
and check if it works.

Related

Swift Codable: How do I make multiple objects from embedded data

Say I have JSON like this:
["conferences": <__NSArrayI 0x60c00002f720>(
{
alias = Conference1;
divisions = (
{
alias = "Division 1";
id = "b95cd27d-d631-4fe1-bc05-0ae47fc0b14b";
name = "Division 1";
teams = (
{
alias = OXC;
id = "768c92aa-75ff-4a43-bcc0-f2798c2e1724";
market = East;
name = Rams;
references = (
{
id = Rams;
origin = gsis;
}
);
{
alias = AXC;
id = "768c92aa-75ff-4a43-bcc0-f2798c2e1724";
market = East;
name = Platypus;
references = (
{
id = Platypus;
origin = gsis;
}
);
},
{
alias = "Division 2";
id = "b95cd27d-d631-4fe1-bc05-0ae47fc0b14b";
name = "Division 2";
teams = (
{
alias = NXC;
id = "768c92aa-75ff-4a43-bcc0-f2798c2e1724";
market = West;
name = Ants;
references = (
{
id = Ants;
origin = gsis;
}
);
{
alias = QXC;
id = "768c92aa-75ff-4a43-bcc0-f2798c2e1724";
market = West;
name = Bulls;
references = (
{
id = Bulls;
origin = gsis;
}
);
},
}]
I'm at a lost as to how to create an object that contains the team data but also the Conference and Division alias.
I started down this path but this seems like it'll just basically dump all the json onto the object. I don't want that overhead obviously.
struct Team:Codable {
var arrConference:[Conference]
private enum CodingKeys: String, CodingKey {
case arrConference = "conferences"
}
struct Conference:Codable {
var conferenceName:String
var conferenceID:String
private enum CodingKeys: String, CodingKey {
case conferenceName = "alias"
case conferenceID = "id"
}
}
}
I guess I am stuck thinking in an old way, but how do you enumerate through the data with a codable?

Parsing arrary of dictionaries from dictionary

After parsing a XML file the output is coming in following format in a dictionary
Now I want to convert it into array of dictionaries
Can anyone suggest a proper solution
Printing description of rootDictionary:
<CFBasicHash 0x8866000 [0x1078b38]>{type = mutable dict, count = 1,
entries =>
0 : <CFString 0x8869490 [0x1078b38]>{contents = "Data"} = <CFBasicHash 0x88695c0 [0x1078b38]>{type = mutable dict, count = 1,
entries =>
0 : <CFString 0x8869610 [0x1078b38]>{contents = "row"} = (
{
Address = {
text = "Skt. Clemens Torv 2-6";
};
City = {
text = "Aarhus C";
};
Country = {
text = Danmark;
};
Description = {
text = Ottopiste;
};
Lat = {
text = "56.156818";
};
Lon = {
text = "10.2092532";
};
Name = {
text = Ottopisteet;
};
Type = {
text = 1;
};
Zip = {
text = 8000;
};
},
{
Address = {
text = "Kauppatie 66";
};
City = {
text = Kauhava;
};
Country = {
text = Finland;
};
Description = {
text = "(null)";
};
Lat = {
text = "63.1001586";
};
Lon = {
text = "23.0463634";
};
Name = {
text = I m Here;
};
Type = {
text = 2;
};
Zip = {
text = 62200;
};
}
)
}
}
Now the output dictionary is in above format now again I want to convert it into array of dictionaries
Actual format of XML for reference:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Data>
<row>
<Lat>56.156818</Lat>
<Lon>10.2092532</Lon>
<City>Aarhus C</City>
<Address>Skt. Clemens Torv 2-6</Address>
<Zip>8000</Zip>
<Country>Danmark</Country>
<Name>Ottopisteet</Name>
<Description>Ottopiste</Description>
<Type>1</Type>
</row>
<row>
<Lat>63.1001586</Lat>
<Lon>23.0463634</Lon>
<City>Kauhava</City>
<Address>Kauppatie 66</Address>
<Zip>62200</Zip>
<Country>Finland</Country>
<Name>I am Here</Name>
<Description>(null)</Description>
<Type>2</Type>
</row>
</Data>
DO this:
NSMutableArray *arrData = [[parsedValueDictionary objectForKey:#"Data"] objectForKey:#"row"];
You could just iterate on the dictionary of rows and add each object to an array:
NSMutableArray *arrayOfDictionaries = [[NSMutableArray alloc] init];
NSDictionary *rows = [rootDictionary objectForKey:#"Data"];
[rows enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
[arrayOfDictionaries addObject:obj];
}];
UPDATE: I'm assuming that there are just "row" entries in the "Data" dictionary. Otherwise you should perform a check on the key to si if it's a "row" before.

check the returned value is not NULL

I trie to check if the returned value from objectForKey is not NULL
if([[fbResult objectForKey:#"current_location"]objectForKey:#"country"])
formViewController.country=[[fbResult objectForKey:#"current_location"]objectForKey:#"country"];
But i have an error on the first line
UPDATE
fbResult is a JSON string,if the user has puted ​​his city and his country in facebook it look like :
"birthday_date" = "07/*****9";
"contact_email" = "***mehdi#hotmail.fr";
"current_location" = {
city = Tunis;
country = Tunisia;
id = 11166369***;
name = "Tunis, Tunisia";
state = Qabis;
zip = "";
};
locale = "fr_FR";
name = "Mehdi ****el";
pic = "https://fbcdn-profile-a.akamaihd.net/hprofile-********_s.jpg";
sex = male;
uid = 530****;
}
and if the user did not put his country and city it look like :
{ "birthday_date" = "02/*3/19**";
"contact_email" = "kev**kevin#gmail.com";
"current_location" = "<null>";
locale = "fr_FR";
name = "Aude ***";
pic = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/4***2_**8_1112_s.jpg";
sex = female;
uid = 11***04*;
}
error is
-[NSObject(NSObject) doesNotRecognizeSelector:] + 96
Checking for existence of an object within a dictionary that is nested in another dictionary is not going to catch all occurrences of bad data.
To make your code more solid
id current_location = [fbResult objectForKey:#"current_location"];
if (current_location != nil && [current_location class] != [NSNull class]) {
id country = [current_location objectForKey:#"country"];
if (country != nil) {
formViewController.country= country;
} else {
// current_location has no country
}
} else {
// fbResult has no current_location
}
If the current_location is supposed to be a dictionary you can swap out id currentLocation with NSDictionary * currentLocation
from your update, your current_location can potentially be an instance of NSNull
you can check against that with
[yourObject class] != [NSNull class]

how to parse this in iphone

{
education = (
{
school = {
id = 108102169223234;
name = psss;
};
type = College;
year = {
id = 142833822398097;
name = 2010;
};
}
);
email = "amvijaycse#gmail.com";
"first_name" = Vijay;
gender = male;
id = 100000782204693;
"last_name" = Kumar;
link = "http://www.facebook.com/profile.php?id=100000782204693";
locale = "en_US";
location = {
id = 106377336067638;
name = "Bangalore, India";
};
name = "Vijay Kumar";
timezone = "5.5";
"updated_time" = "2010-11-21T07:45:11+0000";
}
i needed email,firstname,lastname.
[EDIT: Removed because it was inaccurate]

parse value from a dictionary/list

I have a dictionary/list balance_sign_dict from which I need to retrieve 2 values based on condition.
The source XML for this is;
<Value>
<Signature>-873</Signature>
<Amount>1501042000</Amount>
</Value>
I want to parse data from the dict shown below;
Could you please tell me how do I parse conditional data (e.g. if I want the value of "Amount" node for "Signature" node having -873) i.e. it should return me 1501042000
I want a generic way of parsing the values from the dictionary below.
Printing description of balance_sign_dict:
(
{
nodeChildArray = (
{
nodeContent = "-873";
nodeName = Signature;
},
{
nodeContent = 1501042000;
nodeName = Amount;
}
);
nodeName = Value;
},
{
nodeChildArray = (
{
nodeContent = "-1228";
nodeName = Signature;
},
{
nodeContent = 428586000;
nodeName = Amount;
}
);
nodeName = Value;
},
{
nodeChildArray = (
{
nodeContent = "-1140";
nodeName = Signature;
},
{
nodeContent = 79370000;
nodeName = Amount;
}
);
nodeName = Value;
},
Here is what I am trying now;
for (NSDictionary *valueNode in balance_sign_dict){
for (NSArray *nodeChildArray in [valueNode objectForKey:#"nodeChildArray"]){
NSString *tmpNodeName = [nodeChildArray objectAtIndex:1];
NSDictionary *tmpD = [valueNode objectAtIndex:1];
if ([tmpNodeName isEqualToString:#"Signature"]) {
tmpSignVal = [nodeChildArray objectAtIndex:0];
if ([tmpSignVal isEqualToString:#"-873"]) {
tmpSignAmt = [nodeChildArray objectAtIndex:1];
}
}
But for some reason the 2nd part of nodeChildArray gets removed...i.e. nodeContent = 1501042000;
nodeName = Amount;
I am not sure how to access that part.
Please help.
}
}
Actually here is what i get during alternate runs;
Printing description of nodeChildArray:
{type = mutable dict, count = 2,
entries =>
0 : {contents = "nodeName"} = {contents = "Signature"}
1 : {contents = "nodeContent"} = {contents = "-1507"}
}
Printing description of nodeChildArray:
{type = mutable dict, count = 2,
entries =>
0 : {contents = "nodeName"} = {contents = "Amount"}
1 : {contents = "nodeContent"} = {contents = "631000"}
}
How do I get the Amount value for matching Signature value?
NSArray and NSDictionary are the best container classes that I know of in any language that I am experienced with. NSDictionary can store any object or primitive, including an NSArray, and vice versa. When I say dictionary I am arbitrarily referring to either class.
When you output the description of any dictionary (which is as simple as NSLog(#"%#",myDict);, the entire contents of the dictionary are displayed like an XML (or maybe more accurately a JSON) document. It is easy to look at the content of the dictionary description to determine what is contained inside. () denotes an NSArray and {} denotes an NSDictionary.
// this is the beginning of an array.
(
// this is the beginning of index 0 of the array, an `NSDictionary`
{
// objectForKey:#"nodeChildArray"] isKindOfClass:[NSArray class]]
nodeChildArray = ( // inside nodeChildArray is another array
{ // index 0 of array is a dictionary with two keys
// objectForKey:#"nodeContent" == #"-873"
nodeContent = "-873";
// objectForKey:#"nodeName" == #"Signature"
nodeName = Signature;
}, // end of index 0
{ // index 1 of array
nodeContent = 1501042000;
nodeName = Amount;
} // end of index 1
); // end of nodeChildArray, an NSArray stored at objectForKey:#"nodeChildArray"
// this is the only other element in the dictionary, a string
// objectForKey:#"nodeName"] isKindOfClass:[NSString class]]
nodeName = Value;
}, // end of the dictionary containing nodeChildArray and nodeName
{ // objectAtIndex:1
nodeChildArray = (
{
nodeContent = "-1228";
nodeName = Signature;
},
{
nodeContent = 428586000;
nodeName = Amount;
}
);
nodeName = Value;
},
{ // objectAtIndex:2
nodeChildArray = (
{
nodeContent = "-1140";
nodeName = Signature;
},
{
nodeContent = 79370000;
nodeName = Amount;
}
);
nodeName = Value;
},
);
So you can see from looking at your NSDictionary debug output that it's actually an NSArray. The order of structures is NSArray->NSDictionary->NSArray->NSString <--> [[[[NSArray objectAtIndex:i] NSDictionary objectForKey:#"nodeChildArray"] NSArray objectAtIndex:j] NSDictionary objectForKey:#"nodeContent"], finally returning an NSString. Use this as the most recent code - in the last version of the code I passed balance_sign_dict as an NSDictionary. This might have even worked! But it is much better form to cast the argument properly. If you don't know whether or not your dictionary is going to start with an NSArray or an NSDictionary, you can cast it as an id and check its type with [theIdObject isKindOfClass:[{NSArray,NSDictionary} class]]. Don't use the {} and choose one of the two in your code.
-(NSDictionary*)parseBalance:(NSArray*)balance_sign_dict
{
NSMutableDictionary* theResults = [NSMutableDictionary dictionary];
for( NSDictionary* nodeDict in balance_sign_dict )
{
NSArray* nodeChildArray = [nodeDict objectForKey:#"nodeChildArray"];
[theResults addObject:[[nodeChildArray objectAtIndex:1] objectForKey:#"nodeContent"] forKey:[[nodeChildArray objectAtIndex:0] objectForKey:#"nodeContent"]];
}
return theResults;
}