解析 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,有时获取数字而不是字符串的主要内容,如果未能解决你的问题,请参考以下文章