0

The following code designed to catch an NSNull in Json results is throwing an exception when the response is null.

 NSDictionary *jsonResults = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
    NSLog(@"jsonResults are:%@",jsonResults);
                if (![jsonResults isKindOfClass:[NSNull class]]&&!
[jsonResults[@"response"][@"insert_id"]isKindOfClass:[NSNull class]]&&!
(jsonResults==nil)){
    //do something
    }

When the exception occurs, the line beginning if (![json... is in green and the error message reads:

     Results: {
        code = 400;
        response = "0(NSNull)";
    }
    2016-05-26 07:18:06.327 idaru[385:60b] -[NSNull 
objectForKeyedSubscript:]: unrecognized selector sent to instance 
0x38871a70
    2016-05-26 07:18:06.329 myapp[385:60b] *** Terminating app due to 
uncaught exception 'NSInvalidArgumentException', reason: '-[NSNull 
objectForKeyedSubscript:]: unrecognized selector sent to instance 
0x38871a70'

Can anyone suggest what could be wrong here?

Of note, I do have a category that supposedly converts NSNulls into 0s. Not sure how this interacts with above but here it is:

//NSNull+JSON.m
@implementation NSNull (JSON)

- (NSUInteger)length { return 0; }

- (NSInteger)integerValue { return 0; };

- (float)floatValue { return 0; };

- (NSString *)description { return @"0(NSNull)"; }

- (NSArray *)componentsSeparatedByString:(NSString *)separator { return @[]; }

- (id)objectForKey:(id)key { return nil; }

- (BOOL)boolValue { return NO; }

@end
zztop
  • 701
  • 1
  • 7
  • 20

3 Answers3

1

You are not checking if response is NSNull. My approach would be to test for what you want, not what you don't want:

NSDictionary *jsonResults = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
if (jsonResults) {    // This won't be NSNull, it will be nil on error
    NSDictionary *response = jsonResults[@"response"];
    if ([response isKindOfClass:[NSDictionary class]]) {
        NSNumber *insertId = response[@"insert_id"];    // Assumption
        if ([insertId isKindOfClass:[NSNumber class]]) {
            // Print value
        }
    }
} else {
    // Report error
}

In reality you need to know what values are optional and what values can be of different types, as you don't want to code the whole module using isKindOfClass.

trojanfoe
  • 120,358
  • 21
  • 212
  • 242
  • This approach makes sense so up voted but would require moving a lot of code as the //do something has a lot of stuff in it. – zztop May 26 '16 at 11:46
  • marking correct because you pointed out I need to check jsonResults[@"response"] in addition to other two levels – zztop May 26 '16 at 13:10
  • My apologies for marking correct and unmarking. Error only ocurs with a slow network and I thought it was working. But it is not. So still have not solved problem. – zztop May 26 '16 at 16:51
1

try like this,

  if (![jsonResults isKindOfClass:[NSNull class]] || !(jsonResults==nil)) {

    if (![jsonResults[@"response"][@"insert_id"]isKindOfClass:[NSNull class]]) {

        //Do your task here
    }

}

It is because if your jsonResults is null then it can't check for [jsonResults[@"response"][@"insert_id"] !!

Update :

   if (!(jsonResults==nil)) {

    if (![jsonResults[@"response"][@"insert_id"]isKindOfClass:[NSNull class]]) {

        //Do your task here
    }

}

No need to check for NSNull for jsonResults. It only can be nil and objects contains by that jsonResults can be NSNull.

Hope this will help :)

Ketan Parmar
  • 27,092
  • 9
  • 50
  • 75
  • I combined the two into if ((![jsonResults isKindOfClass:[NSNull class]] || !(jsonResults==nil))&&![jsonResults[@"response"][@"insert_id"]isKindOfClass:[NSNull class]]) {... and it seemed to work – zztop May 26 '16 at 11:45
  • I cannot see how this answer is right as `JSONObjectWithData` will return an array, a dictionary or `nil`, and never `NSNull` so I don't see how this fixes the issue. – trojanfoe May 26 '16 at 11:49
  • @trojanfoe : It can be `NULL` as you can set `jsonResults = NULL;` in ios. and `nil` also defines the id of null instance. – Ketan Parmar May 26 '16 at 11:58
  • 1
    No I said it cannot be `NSNull` which is all your code checks for compared to the OPs. Also that `||` in the `if` statement is wrong. – trojanfoe May 26 '16 at 12:07
  • See, json format must have one from two top level object : `NSArray or NSDictionary` and `All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.` check [Apple documentation](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSJSONSerialization_Class/) – Ketan Parmar May 26 '16 at 12:22
  • So you agree that the root object cannot be `NSNull`? Therefore why check for it? Also statements like "It is because if your jsonResults is null then it can't check for ..." makes me think you don't understand what happens when you send a message to a `nil` object... – trojanfoe May 26 '16 at 12:38
  • @trojanfoe : Yes, that's little misunderstood. root object can not be `NSNull`. Updated answer accordingly. and thanks for suggestion. :) – Ketan Parmar May 26 '16 at 12:41
  • OK, so now how does the updated code improve upon that provided by the OP? If `jsonResults == nil`, what does doing `id value = jsonResults[@"response"];` do? – trojanfoe May 26 '16 at 12:46
  • My earlier mistake. Answer did not fix problem. I think problem may have to do with category that changes an NSNull to "0(NSNull)" so it is neither nil nor NSNull – zztop May 26 '16 at 12:47
  • @zztop The issue is that `jsonResults[@"response"]` holds an `NSNull` object. This check is covered in my answer. The exception is telling you that it *is* an `NSNull` object. – trojanfoe May 26 '16 at 12:48
  • @trojanfoe : Now, If jsonResult is not nil then it checks for it's element that it is `NSNull` or not, if not then perform task else not! – Ketan Parmar May 26 '16 at 12:50
  • Ok. But when I just test for [NSNull null] or isKindOfClass:[NSNull class]] it passes test. – zztop May 26 '16 at 12:51
  • That isn't what your code says. You test the top-level (`jsonResults`) for `NSNull` but not `jsonResults[@"response"]`. Because you don't check this, and immediately do `jsonResults[@"response"][@"insert_id"]` you get the exception. – trojanfoe May 26 '16 at 12:52
  • @zztop : you can check for every elements of `jsonResults` that it is null or not as i mentioned in answer or you can check for desired class like check that if it is `NSDictionary` of `NSString` class as mentioned in @trojanfoe's answer – Ketan Parmar May 26 '16 at 12:55
  • @Lion Your code does exactly the same thing and assumes `jsonResults[@"response"]` is valid and immediately attempts to get the `@"insert_id"` element from it, causing an exception. Anyway I've had enough of trying to convince you and the OP where the mistake is, so I will comment no further on this issue. – trojanfoe May 26 '16 at 12:57
  • @trojanfoe have you see my update in answer? in that not checking for `nsnull` for `jsonResuls`! – Ketan Parmar May 26 '16 at 12:57
  • It sounds like I also need to check that jsonResults[@"response"] is not nil. In other words I must check three things, jsonResults not nil, jsonResults[@"response"] not nil and jsonResults[@"resonse"[@"insert_id"] is not NSNull – zztop May 26 '16 at 13:01
  • yes, you need to check that it is also nil. It's better to check accroding to @trojanfoe's answer – Ketan Parmar May 26 '16 at 13:21
  • Actually it is still not working. (The error is only duplicated when there is a network error. ) I think the error has to do with the category. – zztop May 26 '16 at 13:37
  • you should start with if(!error){//do your task here} else {//handle error like show alertview and print error description} – Ketan Parmar May 26 '16 at 17:03
0

Instead of [jsonResults isKindOfClass:[NSNull class]] you can simply do jsonResults == [NSNull null]

KIDdAe
  • 2,714
  • 2
  • 22
  • 29