Parsing JSON data to an array - objective-c

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.

Related

How to handle JSON exception objective c

Before I make url, which I will use to fetch json, user has to input some data first.
If the input data is wrong, the JSON will not be fetched properly.
But I cannot figure out how to handle that exception of calling JSON with wrong url.
this is my code:
NSError *error;
NSMutableDictionary* json = [NSJSONSerialization
JSONObjectWithData:url
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:&error];
if (error){
NSLog(#"%#",[error localizedDescription]);
}
else{
#try {
if (json){
[Constants shared].salt = json[#"salt"];
The last line of code is where the exception occurs, since the user had put the wrong input.
SO obviously, there wont be a proper json response fetchet, and there will be no "salt" object.
Error I get is:
-[__NSArrayM objectForKeyedSubscript:]: unrecognized selector sent to instance 0x146aa310
I tried putting json fetching in #try #catch, but it didn't work out.
Any suggestions ?
EDIT:
this is the json responce I get, when the user types in the right code:
{
user_id: "22066",
salt: "ce8c0f9e3e1add06bebc1acded7b692b68efddb87bfdc5bb1fb516f6a3e24425"
}
This is what i get, when the code is invalid:
[ ] (empty array)
The problem isn't that the dictionary lacks an object for the key #"salt", it's that json isn't a dictionary in the first place. Take a close look at the error message and you'll see that it's an array. The problem isn't the key, but the fact that arrays don't respond to -objectForKeyedSubscript:.
Accordingly, when you get an object back from -JSONObjectWithData:..., check that it's not nil and that it is in fact a dictionary before you try to access its contents. You can check it like this:
if ([json isKindOfClass:[NSDictionary class]]) {
// put the code that accesses `json` here
}
That condition will be false if json is nil or something other than a dictionary. You could add an else clause to take any necessary steps to recover if you're relying on getting the data.

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 ;)

Exception when trying to fetch channelId using the youtube api in objective c

I am trying to fetch youtube channel id using the google-api-objectivec-client. The problem I am having is basically that for some reason I am receiving exception when trying to access the channelId. The code I am using:
GTLServiceYouTube *service = [[GTLServiceYouTube alloc] init];
service.APIKey = _MY_API_KEY_;
GTLQueryYouTube *query = [GTLQueryYouTube queryForSearchListWithPart:#"id"];
query.q = #"google";
query.type = #"channel";
query.maxResults = 1;
GTLServiceTicket *ticket = [service executeQuery:query completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
if (error == nil) {
GTLYouTubeSearchListResponse *products = object;
for (id item in products.items) {
GTLYouTubeSearchResult *result = item;
NSLog(#"Identifier:%#",result.identifier);
GTLYouTubeResourceId* resourceId = result.identifier;
NSLog(#"kind:%#",resourceId.kind);
NSLog(#"channel:%#",resourceId.channelId);
}
}else{
NSLog(#"Error: %#", error.description);
}
}];
The output I get when i am running this code is:
2013-04-05 11:37:12.615 YouTest[21704:11303] Identifier:GTLYouTubeChannel 0x7233b00: {kind:"youtube#channel" channelId?:"UCK8sQmJBp8GCxrOtXWBpyEA"}
2013-04-05 11:37:12.617 YouTest[21704:11303] kind:youtube#channel
2013-04-05 11:37:12.617 YouTest[21704:11303] -[GTLYouTubeChannel channelId]: unrecognized selector sent to instance 0x7233b00
2013-04-05 11:37:12.618 YouTest[21704:11303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[GTLYouTubeChannel channelId]: unrecognized selector sent to instance 0x7233b00'
So my implementation crashes on the point where I am trying to access the channelId of the resourceId. From the documentation I understood that the channelId should be there as the type of the resourceId is youtube#channel. The channelId can be off course parsed from the result.identifier string that I am also printing, but since there is a property for the channelId I would prefer using that.
Any ideas about what is wrong with my code?
There is indeed a bug in the Google libraries. However I solved this problem by accessing the JSON string directly and parsing it with the help of the NSString+SBJSON.h class, as in this example.
#import "NSString+SBJSON.h"
...
GTLYouTubeResourceId *resource = channel.snippet.resourceId;
NSDictionary *jsonObject = [resource.JSONString JSONValue];
NSString *channelid = [jsonObject valueForKey:#"channelId"];
I'm not very familiar with Objective-C, but yeah, that looks like there's something wrong with the generated client library's YouTube Data API v3 bindings. Are you using the latest version from the project page? You might want to file a bug against the client library if you can reproduce it with the latest version. While troubleshooting this further, I'd check to see if you have the same problem when query.type = #"video"; and you try to access the videoId of the response item.
Here's an alternative you could try, though. The channel's id is also returned in the snippet.channelId property. If you request the snippet part via GTLQueryYouTube *query = [GTLQueryYouTube queryForSearchListWithPart:#"snippet"]; see if you can read that value instead.
I had the same issue. Solved it with the following...
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:[resourceId.JSONString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
NSString *channelId = [jsonObject valueForKey:#"channelId"];
NSLog(#"channelId is %#", channelId);
Workaround Code:
channel.snippet.resourceId.JSON[#"channelId"];
No need to parse the JSON yourself as the underlying JSON is exposed.
It looks like the automatic binding is not working for GTLYouTubeResourceId because the "kind" element of "youtube#channel" is throwing off the runtime object creation and creating a GTLYouTubeChannel instead.
Thorough Workaround Code:
ticket.surrogates = #{ (id)[GTLYouTubeChannel class] : [GTLYouTubeResourceId class] };
If you really want to force that binding to work you can workaround a little further upstream on the ticket when you execute the query.
Global Workaround Patch:
https://github.com/google/google-api-objectivec-client/pull/109
There's open tickets for the issue:
https://github.com/google/google-api-objectivec-client/issues/63
https://github.com/google/google-api-objectivec-client/issues/92
It seems they want to change the API to not call the resourceId.kind 'kind' to avoid this problem. But while we wait for the API to change, any of these three workarounds should serve your purposes.

Parse JSON with iOS 6 and xCode 4.5.2

I'm new in objective-c and and building a sign in page for a specific website. When the user provide information the following JSON return back from the server:
{
"object":{},
"resultCode":0,
"resultMessage":"You successfully signed in "
}
And this what I'm trying to do based on a tutorial I found:
-(IBAction)login{
// Download JSON
NSData *data = [NSData dataWithContentsOfURL: [NSURL URLWithString:[NSString stringWithFormat:#"https://www.example.com/login?username=%#&password=%#", nationalID.text,passowrd.text]]];
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSArray *response = [json objectForKey:#"resultMessage"];
NSLog(#"response %#",response);
}
I want to log out "resultMessage" but somehow it throws an exception:
2012-12-13 06:46:50.607 myProject[7178:11303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'data parameter is nil'
*** First throw call stack:
(0x1c8d012 0x10cae7e 0x1c8cdeb 0xbff817 0x2f1a 0x10de705 0x15920 0x158b8 0xd6671 0xd6bcf 0xd5d38 0x4533f 0x45552 0x233aa 0x14cf8 0x1be8df9 0x1be8ad0 0x1c02bf5 0x1c02962 0x1c33bb6 0x1c32f44 0x1c32e1b 0x1be77e3 0x1be7668 0x1265c 0x279d 0x26c5)
libc++abi.dylib: terminate called throwing an exception
Looks like the -dataWithContentsOfURL: message failed and returned nil, which is exactly what the error message says. You need to confirm that your -stringWithFormat: message is giving you the correct URL format, and check whether the server is returning what you expect.
if you could observe the issue description:
'NSInvalidArgumentException', reason: 'data parameter is nil'
means the response from your response from the web url is nil or NULL
this is not a issue related to JSON parser.
Looks like you have a simple typo, you are using:
nationalID.text,passowrd.text
You probably meant to not misspell 'password'.
First Try static url like:
https://www.example.com/login?username=admin&password=123
if this returns you correct response then try Property Variables like:
https://www.example.com/login?username=self.username.text&password=self.password.text
And one mistake you have made is that resultMessage is string not an Array.
Hope this helps you

Xcode json error

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?