IOS: How to check json object is not null - ios

I am getting an error with the following code when some json is null even though I am trying to check that first:
Edit: Preceding Code:
NSData* data = [NSData dataWithContentsOfURL: kItemsURL];
//previous line grabed data from api.
if (data) {
[self performSelectorOnMainThread:#selector(fetchData:) withObject:data waitUntilDone:YES];
}
- (void)fetchData:(NSData *)jsonFeed {
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:jsonFeed options:kNilOptions error:&error];
//Original code provided
if (![[json objectForKey:#"items"] isKindOfClass:[NSNull class]]) {
NSLog(#"got here");
NSLog(#"json%#",json);
latestItems = [[json objectForKey:#"items"]mutableCopy];
}
Is there a better way to check Json is not null?
Here is error output:
2016-05-03 13:05:43.820 testApp[407:60b] got here
2016-05-03 13:05:43.821 testApp[407:60b] json{
items = "<null>";
}
NSNull mutableCopyWithZone:]: unrecognized selector sent to instance 0x3ac26a70
2016-05-03 13:05:43.825 ChallengeU[407:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull mutableCopyWithZone:]: unrecognized selector sent to instance 0x3ac26a70'

if (![json[#"items"] isEqual:[NSNull null]]) {
//do your stuff in here
}

I don't know what's wrong with your code, but the easiest way to check for a JSON null value if you are sure that json is a dictionary is:
if (json [#"items"] != [NSNull null]) { ... }
[NSNull null] always returns the same instance of NSNull. There is never more than one instance of NSNull, so you can actually use pointer comparison to check whether an object is an NSNull instance.

if (json && [json isKindOfClass:[NSDictionary class]]) {
//your code here
} else {
//invalid json
}

if ([json objectForKey:#"items"] != nil) {
NSLog(#"got here");
NSLog(#"json%#",json);
latestItems = [[json objectForKey:#"items"]mutableCopy];
}
NULL or nil is not a class, but a convention. The memory adress 0 is the point where a cpu starts to execute code during cold start. Therefore no object can have this address in memory.
By the book should the json not have the key #"items" if the value of #"items" is NULL. The missing key indicates a NULL value.

Related

iOS: Looking for NSDictionary value I get error: [__NSCFNumber isEqualToString:]: unrecognized selector sent to instanc

I'm getting Json response with subArrays of dictionaries and I'm looking for particular value inside of one the subArrays. This is the subArray I care about:
<__NSArrayM 0x600000047680>(
{
value = "VeryImportanValue";
},
{
value = "someValue";
},
{
value = 0912131235235234;
}
)
I'm looking for the dictionary with the value of "VeryImportanValue". But as you can see you of the values are numeric and I check:
if ([[dict objectForKey:#"value"]isEqualToString:#"VeryImportanValue"])
I get this error:
-[__NSCFNumber isEqualToString:]: unrecognized selector sent to instance
But switch the if statement to fix the error above:
if ([[[dict objectForKey:#"value"] stringValue] isEqualToString:#"VeryImportanValue"])
I get this error:
-[__NSCFString stringValue]: unrecognized selector sent to instance
How can I check the value of the dictionary without getting any of this errors regarless if is string or numeric value?
I'll really appreciate your help.
You could create a method that turns the unknown object into a string:
- (NSString *)stringForStringOrNumber:(id)object
{
NSString *result = nil;
if ([object isKindOfClass:[NSString class]]) {
result = object;
} else if ([object isKindOfClass:[NSNumber class]]) {
result = [object stringValue];
} else {
result = #"I can't guess";
}
return result;
}
Before you do isEqualToString, you should first check the value type of your dictionary.
if(dict[#"value"]isKindOfClass==[NSString class]) //value is string

Error : unrecognized selector sent to instance

I have been through similar questions, but not able to solve my issue.
The issue is i have a call to API and i am storing the response in a NSDictionary, the response is a single value like RED or GREEN:
saveBlock:^( NSDictionary *data, NSError **err )
{
if (! *err)
{
if (data)
{
NSLog(#"KEY %# - VALUE %#",data.allKeys,data.allValues); //Error because i am performing .allKeys on type NSString
NSString *str = data; // error because i am assigning NSDictionary to string.
}
}
else
{
NSLog(#" error is : - %#",*err);
}
}];
Aren't these two error messages contradicting each other ? Anyone explanation about this issue. Thank you.
EDIT The first NSLog gives this error : *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString allKeys]: unrecognized selector sent to instance 0x170664b80'
(This error appears only at runtime and not when i type data.allKeys)
And the second NSLog gives this: Incompatible pointer type, initialising NSString* with an expression of type NSDictionary *
(This is shown after typing, i don't know what this type of error is called :D)
It is a standard practice to check your response type and error message before performing any operation, sort of defensive coding.
eg.
DLog(#"Object:%#,Error:%#",responseObject,error);
if (error != nil) {
DLog(#"Got error from API:%#",error);
return ;
}
if (!responseObject) {
DLog(#"Response object is not valid, returning...");
return;
}
if (![responseObject isKindOfClass:[NSDictionary class]]) {
DLog(#"Incorrect type of returned object, expecting NSDictionary, got:%#",[responseObject class]);
return;
}
Also I am pretty sure you are getting a NSString cause you don't get an error when assigning a NSDictionary to a NSString
Webservice response data 'data' should be NSDictionary, but in your case you are getting NSString. For NSString class, there is no allKeys method available, that is why you are getting crash. One thing is to validate for NSDictionary like.
if (data && [data isKindOfClass:[NSDictionary class]]) {
NSLog(#"KEY %# - VALUE %#",data.allKeys,data.allValues); //Error because i am performing .allKeys on type NSString
NSString *str = data; // error because i am assigning NSDictionary to string
}
Or remove NSLog

iOS How to check if valueForKey exist or not?

I have another question about checking the existence of valueForKey.
Here's my code:
id _jsonId = [NSJSONSerialization JSONObjectWithData:[_json dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&_error];
Monday = [[NSMutableArray alloc] init];
if ([_jsonId valueForKey:#"Mon"]!= Nil) {
_keyMon = [_jsonId valueForKey:#"Mon"];
[Monday addObject:#"Monday"];
for (int i=0; i<5; i++) {
if ([_keyMon objectAtIndex:i]!=[NSNull null]) {
[Monday addObject:[_keyMon objectAtIndex:i]];
}
}
} else {
[_keyTues addObject:#"No Class For Today"];
[Monday addObject:[_keyMon objectAtIndex:1]];
}
The idea is, if the _jsonId valueForKey came back nothing, I can add this NSStringaddObject:#"No Class For Today incase XCode tells me that:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
like it has been telling me now. Anyone have any solution?
I tried if ([_jsonId valueForKey:#"Mon"]!= Nil) didn't work.
I tried if ([_jsonId valueForKey:#"Mon"]!= nil) didn't work.
I tried if ([_jsonId valueForKey:#"Mon"]!= (id)[NSNull null]) didn't work.
I tried if ([_jsonId valueForKey:#"Mon"]!= [NSNull null) didn't work.
I know that _jsonId valueForKey is came back as a pointer, but I have no idea how to check if the pointer is null(or nil in Objective-C if I'm correct). Please help me.
UPDATE:
I have tried using objectForKey and here is a code(_jsonId objectForKey is nil, that I know to try testing the code)
if (_jsonId[#"Sun"]== nil) {
[_keySun addObject:#"No Class For Today"];
[Monday addObject:[_keySun objectAtIndex:1]];
}
else{
_keySun = [_jsonId objectForKey:#"Sun"];
[Monday addObject:#"Sunday"];
for (int i=0; i<5; i++) {
if ([_keySun objectAtIndex:i]!=nil) {
[Monday addObject:[_keySun objectAtIndex:i]];
}
}
}
Still, XCode telling me that I'm add a nil into NSArray.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
This is driving me crazy...
_jsonId as defined by:
id _jsonId = [NSJSONSerialization JSONObjectWithData:[_json dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&_error];
... will be either an array or a dictionary. Based on your code I think you're assuming that it's always a dictionary. So you probably want to add:
if (![_jsonId isKindOfClass:[NSDictionary class]]) ...
You then want to know what's in a dictionary, if anything. So use objectForKey:. Or, better, just _jsonId[#"Mon"]. You'll get an object or you'll get nil.
valueForKey: is a completely different mechanism, key-value coding. It is defined on NSObject so inherited by NSDictionary as well as just about everything else. It is defined to raise an exception for an unknown key. Since it raises an exception instead of returning a result, your attempts to compare a result are ineffective.
I think I figured it out. Let's look at the code:
if (_jsonId[#"Sun"]== nil) {
[_keySun addObject:#"No Class For Today"];
[Monday addObject:[_keySun objectAtIndex:1]];
}
else {
_keySun = [_jsonId objectForKey:#"Sun"];
...
If there is an object for the key "Sun" then you store it into _keySun. If there is no object, then you store nothing into _keySun. _keySun will be nil. You send an addObject message to _keySun - but any message sent to a nil object does nothing. You then call [_keySun objectAtIndex:1]. Again, sending a message to a nil object does nothing, and returns nil, which is why you add nil to the Monday object and get a crash.
If you had set _keySun to an empty NSMutableArray, you would have crashed for another reason: You try to add one object. You can read that one object with objectAtIndex:0. But to read objectAtIndex:1 you need two objects in the array.
BTW. You will get [NSNull null] from the JSON parser if the JSON document that you are reading contains an explicit null. So if your JSON contains "Sun": null then objectForKey:#"Sun" will give [NSNull null]. If your JSON doesn't contain the key "Sun" at all, objectForKey:#"Sun" will give nil.

Unrecognized Selector on Calling allKeys

I'm calling the following code on a number of JSON dictionaries within another dictionary (so in this case, NSDictionary objects with another NSDictionary.
-(NSString *)getAllDictionaryValues:(NSDictionary *)dict
{
NSString *output=[NSString stringWithFormat:#""];
for(NSString *key in [dict allKeys])
{
output = [NSString stringWithFormat:#"%#\n%#: %#",output, key, [dict objectForKey:key]];
}
return output;
}
When I run this code, however, I get
2014-03-30 01:27:35.565 WebServiceTest[48606:60b] -[__NSCFString allKeys]: unrecognized selector sent to instance 0x9270230
What is wrong about my call to allKeys?
Like when you sending message to this function it may be declare your code that dict is an NSDicationay object (when you call this function) but it not an dictionary may be an array of String or etc that why you getting the exception.
-(NSString *)getAllDictionaryValues:(NSDictionary *)dict
So first check your dict like
if([dict isKindOfClass:[NSArray class]]){
//is an array
}else if([dict isKindOfClass:[NSDictionary class]]){
//is an dictionary
}else if([dict isKindOfClass:[NSString class]]){
//is an string
}else{
//or its something else
}
May be you are inputting a string rather than a dictionary object in the calling method
That's obvious. As log said, you tried to call allKeys method on __NSCFString instance. It means that you sent NSString instead of NSDictionary to getAllDictionaryValues: method.
It means the dict object is NSString type. You can check the object like this:
if([dict isKindOfClass:[NSDictionary class]]) {
// dict is NSDictionary type of object
} else if([dict isKindOfClass:[NSString class]]) {
// dict is string type of object.
}

[iOS][objC] unable to convert value in NSDictionary to NSString

I intend to convert the NSDictionary* object in iOS SDK to NSString*.
Lets say my NSDictionary object has following key value pairs:
{"aps":{"badge":9, "alert":"hello"}} (notice that value itself is a NSDictionary object)
and I want it to convert into a hash map with key value pair as {"aps":"badge:9, alert:hello"} (notice value is just a string).
I am able to print the value in NsDictionary using the following code:
NSDictionary *userInfo; //it is passed as an argument and contains the string I mentioned above
for (id key in userInfo)
{
NSString* value = [userInfo valueForKey:key];
funct( [value UTF9String]; // my function
}
But i am not able to call any NSString method on value object like UTT8String. It gives me error "Terminating app due to uncaught exception NSInvalidArgumentException: reason [_NSCFDictionary UTF8String]: unrecognised selector sent to instance
You are going to have to recursively process the dictionary structure, here is an example that you should be able to adapt:
-(void)processParsedObject:(id)object{
[self processParsedObject:object depth:0 parent:nil];
}
-(void)processParsedObject:(id)object depth:(int)depth parent:(id)parent{
if([object isKindOfClass:[NSDictionary class]]){
for(NSString * key in [object allKeys]){
id child = [object objectForKey:key];
[self processParsedObject:child depth:depth+1 parent:object];
}
}else if([object isKindOfClass:[NSArray class]]){
for(id child in object){
[self processParsedObject:child depth:depth+1 parent:object];
}
}
else{
//This object is not a container you might be interested in it's value
NSLog(#"Node: %# depth: %d",[object description],depth);
}
}
You need to apply that loop to each child, not the main dictionary. You said yourself you have a dictionary in a dictionary:
for(id key in userInfo)
{
NSDictionary *subDict = [userInfo valueForKey:key];
for(id subKey in subDict)
{
NSString* value = [subDict valueForKey:subKey];
}
}
This loop assumes you have the entire dictionary in the first level, otherwise you will need to use danielbeard's recursive method.
I found the easiest way out. calling the description method on the NSDictionary object gives me what I needed exactly. Stupid to have missed it on first go.

Resources