解析 JSON,有时获取数字而不是字符串

Posted

技术标签:

【中文标题】解析 JSON,有时获取数字而不是字符串【英文标题】:Parsing JSON and sometimes getting number instead of a string 【发布时间】:2014-02-15 10:55:49 【问题描述】:

我有an iPhone app,它以 JSON 格式从不同的社交网络获取用户信息(名字、姓氏、城市)。

这大部分工作得很好,但是其中一个社交网络将城市作为数字而不是字符串返回(实际上我应该再进行一次 REST 调用来将此数字映射到城市名称......但现在我只想显示数字)。

当我尝试display that number in a UILabel 时,我得到了异常(这里是fullscreen):

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) 

所以我去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;

并尝试将其更改为:

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

但随后我收到编译时警告(此处为 fullscreen):

格式指定类型“int”,但参数的类型为“id”

所以我的问题是如何干净利落地解决这个问题(没有 Xcode 警告)和健壮(当获取的 JSON 数据恰好是一个字符串时)?

【问题讨论】:

【参考方案1】:

最快的解决方案是:

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

它将获取字符串或数字的描述并将其转换为字符串。

以后你可能想用:

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

检查您收到的内容并专门使用它。即进行查找并且当您实际上已经有一个字符串时不使用stringWithFormat:(因为它效率低下)。

【讨论】:

谢谢,这是有道理的!我唯一不明白的是:在我的原始代码中,在解析 JSON 后,我将数字分配给 user.city(即用户对象 github.com/afarber/ios-newbie/blob/2/MyAuth/MyAuth/User.h 的 NSString 属性) - 为什么我没有收到任何警告/崩溃?我只是在稍后尝试在UILabel 中显示 user.city 时才得到它们 因为从字典中出来的对象是id 类型,所以编译器很乐意在运行时检查。赋值实际上并没有检查任何东西,因为您并没有真正使用实例,只是传递了一个指针。第一次尝试使用实例是与标签相关的,然后查找要使用的方法,发现NSNumber类上不存在,然后就crash了…… 举个例子:如果我怀疑某个东西可能是 NSNumber 或 NSString,我会写 NSNumber* asNumber = dict [@"city"]; if ([asNumber isKindOfClass:[NSNumber class]]) ... 使用 NSNumber 方法 else NSString* asString = dict [@"city"]; .. 使用 NSString 方法

以上是关于解析 JSON,有时获取数字而不是字符串的主要内容,如果未能解决你的问题,请参考以下文章

objective-c json 解析器:如何解析以字符串而不是括号开头的 json 文件?

Python Json加载()返回字符串而不是字典?

如何从字符串而不是 URL 解析 json 数据

将时间戳数据解析为本地日期和时间的问题

Python获取jsonp数据

使用 Restkit 解析纯文本