Alexander Farber
Alexander Farber

Reputation: 23038

Parsing JSON and sometimes getting number instead of a string

I have an iPhone app which fetches user information (first name, last name, city) in JSON format from different social networks.

This works mostly well, but one of the social networks returns the city as a number instead of a string (actually I should made one more REST call to map this number to a city name... but for now I just want to display the number).

And when I try to display that number in a UILabel I get the exception (here fullscreen):

Xcode screenshot

2014-02-15 11:24:16.194 MyAuth[8872:a0b] -[__NSCFNumber length]: unrecognized selector sent to instance 0xb074f90
2014-02-15 11:24:16.203 MyAuth[8872:a0b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber length]: unrecognized selector sent to instance 0xb074f90'
*** First throw call stack:
(
    0   CoreFoundation                      0x01bb15e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x019348b6 objc_exception_throw + 44
    2   CoreFoundation                      0x01c4e903 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
    3   CoreFoundation                      0x01ba190b ___forwarding___ + 1019
    4   CoreFoundation                      0x01ba14ee _CF_forwarding_prep_0 + 14
    5   Foundation                          0x015798ed -[NSConcreteMutableAttributedString replaceCharactersInRange:withString:] + 39
    6   Foundation                          0x0157a55a -[NSConcreteMutableAttributedString initWithString:attributes:] + 293
    7   UIKit                               0x0084fbc6 -[UILabel _setText:] + 97
    8   UIKit                               0x0084fd84 -[UILabel setText:] + 40
    9   MyAuth                              0x0000a296 -[UserViewController viewDidLoad] + 678
    10  UIKit                               0x007b6318 -[UIViewController loadViewIfRequired] + 696
    11  UIKit                               0x007b65b4 -[UIViewController view] + 35
    12  UIKit                               0x007d03e2 -[UINavigationController _startCustomTransition:] + 778
    13  UIKit                               0x007dd0c7 -[UINavigationController _startDeferredTransitionIfNeeded:] + 688
    14  UIKit                               0x007ddcb9 -[UINavigationController __viewWillLayoutSubviews] + 57
    15  UIKit                               0x00917181 -[UILayoutContainerView layoutSubviews] + 213
    16  UIKit                               0x0070d267 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 355
    17  libobjc.A.dylib                     0x0194681f -[NSObject performSelector:withObject:] + 70
    18  QuartzCore                          0x054e12ea -[CALayer layoutSublayers] + 148
    19  QuartzCore                          0x054d50d4 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    20  QuartzCore                          0x054d4f40 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 26
    21  QuartzCore                          0x0543cae6 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 294
    22  QuartzCore                          0x0543de71 _ZN2CA11Transaction6commitEv + 393
    23  QuartzCore                          0x0543e544 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 92
    24  CoreFoundation                      0x01b794ce __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
    25  CoreFoundation                      0x01b7941f __CFRunLoopDoObservers + 399
    26  CoreFoundation                      0x01b57344 __CFRunLoopRun + 1076
    27  CoreFoundation                      0x01b56ac3 CFRunLoopRunSpecific + 467
    28  CoreFoundation                      0x01b568db CFRunLoopRunInMode + 123
    29  GraphicsServices                    0x031a09e2 GSEventRunModal + 192
    30  GraphicsServices                    0x031a0809 GSEventRun + 104
    31  UIKit                               0x006a2d3b UIApplicationMain + 1225
    32  MyAuth                              0x0000ab7d main + 141
    33  libdyld.dylib                       0x02518725 start + 0
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

So I go to my JSON parsing code:

- (User*)createUserFromJson:(id)json
{
    if (![json isKindOfClass:[NSDictionary class]]) {
        NSLog(@"Parsing response failed");
        return nil;
    }

    NSDictionary *dict = json[@"response"][0];

    User *user = [[User alloc] init];
    user.key       = kVK;
    user.userId    = dict[@"uid"];
    user.firstName = dict[@"first_name"];
    user.lastName  = dict[@"last_name"];
    user.city      = dict[@"city"];          // THE PROBLEMATIC LINE
    user.avatar    = dict[@"photo_big"];
    user.female    = (2 == [dict[@"female"] intValue]);

    return user;
}

and try to change it to:

user.city = [NSString stringWithFormat:@"%d", dict[@"city"]];

but then I get the compile-time warning (here fullscreen):

Format specifies type 'int' but the argument has type 'id'

Warning screenshot

So my question is how to solve this issue cleanly (w/o Xcode warnings) and robust (when fetched JSON data happens to be a string)?

Upvotes: 0

Views: 668

Answers (1)

Wain
Wain

Reputation: 119041

The fastest solution is:

[NSString stringWithFormat:@"%@", dict[@"city"]];

which will take the description of the string or the number and convert that into a string.

In the future you may want to use:

if ([1dict[@"city"] isKindOfClass:[NSNumber class]]) { ...

to check what you have received and work with it specifically. i.e. to do your lookup and to not use stringWithFormat: when you actually already have a string (because it's inefficient).

Upvotes: 3

Related Questions