Xcode json error - objective-c

I'm learning Xcode at the moment and i have a project that is pulling data from a Mysql database using php and passing it to my app via json. In the database all varchars are set to utf8_bin.
here is the php:
header('Cache-Control: no-cache, must-revalidate');
header('Content-type: application/json');
echo json_encode($this->Idea_model->get($id));
here is a snipet of the outputted JSON:
[{"id":"1","title":"JWT blood sucka","objective":"test ","mission":"test","design_time":"80","development_time":"80","votes":"0","user_id":"0","date_created":"2012-08-03","date_modified":"2012-08-03","active":"1"},{"id":"2","title":"ford - liveDealer","objective":"to increce ","mission":"thid id a es","design_time":"80","development_time":"80","votes":"1","user_id":"1","date_created":"0000-00-00","date_modified":"0000-00-00","active":"1"}]
in xcode I'm using this function to pull in the JSON [reference tutorial:
http://www.raywenderlich.com/5492/working-with-json-in-ios-5]
(void)fetchedData:(NSData *)responseData {
//parse out the json data
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
NSArray* latestLoans = [json objectForKey:#"loans"]; //2
NSLog(#"loans: %#", latestLoans); //3
}
when i use this JSON file from the tutorial it works
http://api.kivaws.org/v1/loans/search.json?status=fundraising
but when i use my JSON file i get the following error.
[8690:207] -[__NSCFArray objectForKey:]: unrecognized selector sent to instance 0x6a10400
Current language: auto; currently objective-c
obviously there is an issue with my JSON output as i printed the contents from the tutorial file in my PHP file and that worked as well.
i also have tried "reset contents and settings" in the iOS simulator.
any ideas?

The returned object appears to be an array but your code is treating it like a dictionary (json object/hash)
The error tells you this: it say that the message objectForKey: (which is a method on NSDictionary) is being sent to an instance of __NSCFArray, which is an implementation class of NSArray, hence my supposition...

Yes I have an Idea -
-[__NSCFArray objectForKey:]: unrecognized selector sent to instance 0x6a10400
Arrays are not dictionaries. They do not respond to objectForKey
They respond to objectForIndex;
You are thinking you have an array when you have a dictionary.
Common JSON mistake.

Heres your data:
its a list
starts here --> "[" then the object starts here "{"
[{"id":"1","title":"JWT blood sucka","objective":"test ","mission":"test","design_time":"80","development_time":"80","votes":"0","user_id":"0","date_created":"2012-08-03","date_modified":"2012-08-03","active":"1"}
then a comma "," then the next item in the list starting with a { "{"id":"2","title":"ford - liveDea
JSON says a list is an array and an object is a dictionary so flip your code around
(void)fetchedData:(NSData *)responseData {
//parse out the json data
NSError* error;
NSArray* latestLoans = [NSJSONSerialization
JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
NSLog(#"loans: %#", latestLoans); //3
for (int i=0; i < latestLoans.count; i++)
{
NSDictionary *myLoan = (NSDictionary*)[latestLoans objectAtIndex:i];
NSLog(#"loan:%#", myLoan);
}
....
Got it?

Related

Getting "unrecognized selector sent to instance" in code for ios8

This code work in ios7, but I am now getting the error :
-[__NSCFString objectForKeyedSubscript:]: unrecognized selector sent to instance
.m file
//get the JSON response
NSDictionary *jsonData = [NSJSONSerialization
JSONObjectWithData:urlData
options:NSJSONReadingMutableContainers
error:&error];
//Parses the "success" value
success = [jsonData[#"success"] integerValue];
//Was it successful?
if(success){
//successful, save the profile gathered into global gMyProfile
NSArray *profileJSON=jsonData[#"myProfile"];
for (NSDictionary* dict in profileJSON)
{
NSLog(#"First_Name: %#", dict [#"first_name"]);
...
The error is happening on the NSLog statement and from a little research it is complaining about the dict[#"first_name"]);
login.py
....
#Query for user
db_cursor = db.cursor(MySQLdb.cursors.DictCursor)
db_query = """SELECT users.email, users.first_name, users.profile_pic_path, \
FROM users,data WHERE users.email='%s' \
AND users.user_id=data.user_id""" % user_email
db_cursor.execute(db_query)
#If there is one record containing the username check password
if(db_cursor.rowcount == 1):
user_profile = db_cursor.fetchone()
...
json_obj= {'success': 1, 'myProfile': user_profile,}
...
JSON Output:
{'myProfile': {'first_name': 'Matt', 'email': 'matt#email.com', 'profile_pic_path': 'default'}, 'success': 1}
All this code was working, I have not changed anything.
Any help will be greatly appreciated!!!!
dict is not a dictionary. It is a string. Print it and you'll know which string it is.
More likely than not, your JSON is not what you think it is. In particular, the myProfile value is a string instead of a dictionary.
What happens if fetchone() returns None; do you end up with a string in your JSON?
Changed in the .m
//Was it successful?
if(success){
//successful, save the profile gathered into global gMyProfile
NSArray *profileJSON=jsonData[#"myProfile"];
for (NSDictionary* dict in profileJSON)
{
...
to
//Was it successful?
if(success){
//successful, save the profile gathered into global gMyProfile
NSDictionary* dict=jsonData[#"myProfile"];
//no more for loop
...
The python JSON module returns a single dictionary instead of an array of a single dictionary when there is one element. If there are multiple it returns an array of dictionaries.
Still don't know why this didn't give me trouble in ios7 but did in ios8.

How do I get this value from NSMutableArray?

this is a fairly simple question.
I am using a web service in my app, and the server returns a JSON string to communitcate with the app.
Here is an example response:
{
repsonse = {
message = "Message";
"response_id" = X;
};
}
Using objective-c I want to be able to get what "response_id" is but I am unsure on how to do this.
Here is my code:
NSMutableArray *json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
// Get json value
NSLog(#"%#", json);
if([json[#"response"][#"response_id"] isEqualToString:#"1"]){
return YES;
}else{
return NO;
}
Each time the isStringEqualTo method returns false.
Could somebody help me?
Thanks,
Peter
You have two problems:
json needs to be declared as an NSDictionary, not NSMutableArray since the JSON root is a dictionary, not an array. And you get back an immutable dictionary, not a mutable one.
The JSON has a key of "repsonse", not "response".

Parse JSON String and array with NSJSONSerialization issue?

This is the code i have so far
// Parse data using NSJSONSerialization
NSError *error = nil;
NSArray *JsonArray = [NSJSONSerialization JSONObjectWithData:myData options:NSJSONReadingMutableContainers error: &error];
if(!JsonArray)
{
NSLog(#"Error Parsing Data: %#", error);
}
else
{
for(NSDictionary *event in JsonArray)
{
if([[event description] isEqualToString:#"error"])
{
// Get error number? I am confused by this part
NSLog(#"Element: %#", [event objectForKey:#"error"]);
}
else
{
NSLog(#"Element: %#", [event description]);
}
}
}
this is the JSON Data that parses correctly:
[{data string}, {data strings}]
This only gives me the string "error" and not the int as well:
{"error":0}
I am echoing this data from a PHP script if that helps any. Am i just doing it wrong, or did i miss something?
Your problem is that when you receive an error, you get back an NSDictionary and not an NSArray. This should work:
if ([jsonObject isKindOfClass:[NSArray class]]) {
// no error: enumerate objects as you described above
} else if ([jsonObject isKindOfClass:[NSDictionary class]]) {
// error: obtain error code
NSNumber *errCode = jsonObject[#"error"];
} else {
// something bad's happening
}
Stylistic pieces of advice:
Don't call your object JsonArray, since it's not always an array. Call it jsonObject.
Don't start variable names with capital letters.
Would be great if you had posted the complete JSON document that you are trying to parse, because without that, there is absolutely no chance to figure out whether your code is anywhere near correct. The example [{data string}, {data strings}] that you gave is most definitely not a correct JSON document, so trying to parse it will return nil. {"error":0} is a dictionary with a single key "error" and a value 0. Having dictionaries with a single key is let's say unusual.
A JSON document contains either an array or object (using JSON terms) which will be turned either into an NSArray* or an NSDictionary*. You should know whether you expect an array or dictionary. If you expect an NSArray, check that [jsonObject isKindOfClass:[NSArray class]]. If you expect an NSDictionary, check that [jsonObject isKindOfClass:[NSDictionary class]]. If you don't do that then the wrong JSON document will either crash your app or produce total nonsense.
If you have an array then you will usually iterate through the elements of the array and handle each one in turn. If you have a dictionary you will usually look up keys that you know how to handle. What you are doing, iterating through an array of dictionaries, and checking for a dictionary with a key of "error", that's a very strange design of your JSON document.
And lookup what the "description" method does. "description" is what NSLog calls to find out what to print when it is asked to print an object. For an NSDictionary with a single key "error" and a value 0, it would return something like "error:0" which is of course not the same as "error".
NSDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:myData options:NSJSONReadingMutableContainers error:&error];
NSLog(#"jsonDic: %#", [jsonDic objectForKey:#"string"]);

ERROR happened while deserializing the JSON data

-(void) conn:(NSString *)method{
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
__block NSDictionary *resultBlock = nil;
dispatch_sync(concurrentQueue, ^{
/* Download the json here */
//Create webservice address
NSString *webService = [_baseURL stringByAppendingString:_webService];
//NSLog(#"%#", webService);
//Create error object
NSError *downloadError = nil;
//Create the request
NSMutableURLRequest *req = [self initRequest:webService method:method];
if(req != nil){
//Request the json data from the server
NSData *jsonData = [NSURLConnection
sendSynchronousRequest:req
returningResponse:nil
error:&downloadError];
if(downloadError!=nil){
NSLog(#"DOWNLOAD ERROR %#", downloadError);
}
NSError *error = nil;
id jsonObject = nil;
if(jsonData !=nil){
/* Now try to deserialize the JSON object into a dictionary */
jsonObject = [NSJSONSerialization
JSONObjectWithData:jsonData
options:kNilOptions
error: &error];
}
//Handel the deserialized object data
if (jsonObject != nil && error == nil){
NSLog(#"Successfully deserialized...");
if ([jsonObject isKindOfClass:[NSDictionary class]]){
resultBlock = (NSDictionary *)jsonObject;
//NSLog(#"Deserialized JSON Dictionary = %#", resultBlock);
}
else if ([jsonObject isKindOfClass:[NSArray class]]){
NSArray *deserializedArray = (NSArray *)jsonObject;
NSLog(#"Deserialized JSON Array = %#", deserializedArray);
} else {
/* Some other object was returned. We don't know how to deal
with this situation, as the deserializer returns only dictionaries
or arrays */
}
}
else if (error != nil){
NSLog(#"An error happened while deserializing the JSON data. %#", error);
}else{
NSLog(#"No data could get downloaded from the URL.");
//[self conn:method];
}
}
});
dispatch_sync(dispatch_get_main_queue(), ^{
/* Check if the resultBlock is not nil*/
if(resultBlock != nil){
/*Set the value of result. This will notify the observer*/
[self setResult:resultBlock];
}
});
});
}
Why do I get the following error?
An error happened while deserializing the JSON data. Error
Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be
completed. (Cocoa error 3840.)" (JSON text did not start with array or
object and option to allow fragments not set.) UserInfo=0x20839f80
{NSDebugDescription=JSON text did not start with array or object and
option to allow fragments not set.}
When I change it to
/* Now try to deserialize the JSON object into a dictionary */
jsonObject = [NSJSONSerialization
JSONObjectWithData:jsonData
options:NSJSONReadingAllowFragments
error: &error];
}
I get the following error:
An error happened while deserializing the JSON data. Error
Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be
completed. (Cocoa error 3840.)" (Invalid value around character 0.)
UserInfo=0x20888760 {NSDebugDescription=Invalid value around character
0.}
I changed my connection from LTE to wifi and now I get
504 error and NSLog(#"No data could get downloaded from the URL.");
You should fix these issues in your code first:
Properly check for errors in methods which provide a pointer to a reference to an NSError object as the last parameter, e.g.: - (BOOL) doSomething:(NSError**)error, or -(NSData*) doSomething:(NSError**)error
In order test for an error correctly, you have to check the return value of the method only. Those methods indicate an error condition with a "special return value". For example, they return NO or nil - as always specified in the documentation. Only after the method indicated an error, the provided error parameter contains a meaningful value - that is, it points to an NSError object created by the method. Note that this parameter may also become none NULL when the method succeeded, in which case that has no "meaning".
Web services usually can provide several formats of the requested resource. If you don't specify which format you want the server to encode the resource, you get a default format - which is not necessarily JSON.
In order to be explicit about the desired format of the resource, set a corresponding "Accept" header. For example, if you wish the format in JSON you would set a header: "Accept: application/json" in your request.
Web services may have reasons not to respond with the resource you requested. In order to be sure you got the response that you requested, you need to check the response for status code and MIME type in order to ensure you actually received a JSON response.
It seems, you are a bit uncertain about how to use dispatch functions to your advantage. If you use the synchronous convenient method sendSynchronousRequest:... You certainly need to wrap it in only one dispatch_async function. If you then want to set the result on the main thread, you certainly want to use dispatch_async, not dispatch_sync.
However, it would be an improvement if you would use sendAsynchronousRequest:... instead. And only if you would use NSURLConnection in asynchronous mode and implement the NSURLConnection delegate methods - which I strongly recommend - it would actually become great ;)
So, I think, once you fixed your code, you may be able to answer the original question yourself, or get better error responses from the server, or the error magically disappeared ;)

Parsing JSON data to an array

I get the following error whenI launch my app
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '- [COViewController fetchAppNetData]: unrecognized selector
sent to instance 0x716d200'
Basically I am unable to find out how to parse the JSON data to my array. The structure of my JSON is as follows
{
"meta": {},
"data": []
}
I know that meta is a dictionary and data is an array. But when I try to use the following piece of code I get the above error
- (void)fetchAppNetData:(NSData *)responseData
{
//parse JSON data
NSError *error;
NSDictionary* appNet_json = [NSJSONSerialization
JSONObjectWithData:responseData options:kNilOptions error:&error];
NSArray* appNetTimeline = [[appNet_json objectForKey:#"meta"]
objectForKey:#"data"];
NSLog(#"AppNet Timeline : %#",appNetTimeline);
}
How do I make sure that I can identify the structure of JSON properly next time, so that I can avoid this sort of issue? I am extremely sorry to come up with such kind of doubts
The error has nothing to do with the content of the method -fetchAppNetData:. That method is not even getting called.
The error is saying that you tried to invoke a method of that name on an object that doesn't respond to it. You've sent that message to an instance of class COViewController, but that's evidently not the class that implemented the method you posted.