【问题标题】:Objective-C: Detecting Object Type Correctly In JSON DataObjective-C:在 JSON 数据中正确检测对象类型
【发布时间】:2014-01-19 19:21:00
【问题描述】:

我有一个注册新用户的 JSON 服务。当出现“E-mail in use”或“Taken user name”等错误时,返回以“|”分隔的错误码字符(字符串)。注册成功后,返回注册用户的ID(整数)。

例如,注册的电子邮件和用户名(在一种情况下)错误将返回:

{"error" = "200|300|";}

成功注册会返回:

{"error" = 1234;}

所以我用“|”分割错误字符放入数组,然后显示如下错误:

NSArray *errorCodes = [[jsonData objectForKey:@"error"] componentsSeparatedByString:@"|"];

当有错误/有错误时,这条线很好用。但是当没有错误时,这行代码崩溃如下:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber componentsSeparatedByString:]: unrecognized selector sent to instance 0x7579fc0'

如果我没记错的话,它会崩溃,因为它试图拆分一个实际上不是字符串而是数字的字符串。

所以我尝试通过检查对象类的类型来解决这个问题,例如:

if ([[jsonData objectForKey:@"error"] isKindOfClass:[NSNumber class]]) {
    // success
} else if ([[jsonData objectForKey:@"error"] isKindOfClass:[NSString class]])
    // show errors
}

但是,这些声明都没有涉及。当我尝试检查 [__NSCFNumber class] 而不是 [NSNumber class] 时,它返回错误:

Unknown receiver '__NSCFNumber'; did you mean 'NSNumber'?

[NSString class] 也一样。

如何通过检查对象类型来解决这个问题?我知道我可以使用其他方式,但这种方式似乎是正确的方式,因为我将来可能需要对不同的数据类型进行这种控制。

【问题讨论】:

  • 任何更改 API 响应的机会 - 这感觉不是通知客户请求结果的最清晰和最简单的方法。第一步是遵循 REST 约定并返回合适的 HTTP 状态代码 - 例如。 201 成功创建 400 错误
  • @rist 我同意你的观点,但是我只是客户,没有这个机会。我也不会在错误代码中发送成功消息...
  • 如果它没有进入你的 if 语句,记录它的类。

标签: ios objective-c json nsstring nsnumber


【解决方案1】:

由于__NSCFNumber 是NSNumber 的(私有)子类,因此检查NSNumber 是正确的检查。

由于__NSCFNumber 是私有的,你不应该依赖它。 Apple 的工程师可以决定更改名称,返回另一个类,等等。

如果你坚持要检查__NSCFNumber,你可以使用NSClassFromString()

if([obj isKindOfClass:NSClassFromString(@"__NSCFNumber")])

但我不会那样做。

【讨论】:

  • 我同意……不要那样做。这里发生了其他事情。
【解决方案2】:

NSCFNumberNSNumber 的私有子类,但这是您不必担心的实现细节。

关于您的响应,这不是在错误对象中重新运行 id 的好方法。在这种情况下,error 应该是 nil 或者根本不应该出现,并且你应该有另一个带有 id 的参数,它应该有 id 值。

【讨论】:

  • NSString 是否响应 stringValue?
【解决方案3】:

不用检查error对象的类,你可以检查对象是否响应选择器:

    if ([[response objectForKey:@"error"] respondsToSelector:@selector(componentsSeparatedByString:)]) {
        // show errors
    } else {
        //success
    }

这种方法有时称为Duck typing

【讨论】:

    【解决方案4】:

    使用 isKindOfClass: 进行测试是正确的方法。我已经用以下代码 sn-p 验证了这一点:

        NSString* json = @"{ \"error\" : 1234, \"error_str\" : \"testing\" }";
        NSDictionary* d = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
    
        NSLog(@"error: %d (class %@), error_str: %d (class %@)",
              [d[@"error"] isKindOfClass:[NSNumber class]], [d[@"error"] class],
              [d[@"error_str"] isKindOfClass:[NSNumber class]], [d[@"error_str"] class]);
    

    给出以下输出:

    2014-01-19 12:43:07.236 Test[64222:303] error: 1 (class __NSCFNumber), error_str: 0 (class __NSCFString)
    

    我有几个问题:

    1. 您是否已验证[jsonData objectForKey:@"error"] 在 isKindOfClass: 检查失败时返回非零值?因为如果为 nil,isKindOfClass: 将返回 0。
    2. 您使用的是什么 JSON 解析器? NSJSONSerialization 还是第三方解析器?
    3. 上面列出的 JSON 真的是您要解析的吗?因为它们不是有效的 JSON。例如,{"error" = "200|300|";} 使用等号(应该是冒号)和分号(应该是逗号或什么都没有,因为它是字典中的最后一个元素)。

    【讨论】:

    • Doug,它看起来像解析器给出的 NSDictionary 的 NSLog 输出。看到原始的 JSON 会更好,因为我们无法看到 1234 是字符串还是数字,除了抛出的异常。
    【解决方案5】:
    id error = jsonData [@"error"]; // Could produce anything
    if (error == nil)
    {
        // There wasn't actually any "error" in your json data
    }
    else if (error == [NSNull null])
    {
        // json contained "error": null so you were told there is no error information
    }
    else if ([error isKindOfClass:[NSNumber class]])
    {
        NSNumber* errorAsNumber = error;
        // json contained "error": 1234 or something similar
    }
    else if ([error isKindOfClass:[NSString class]])
    {
        NSString* errorAsString = error;
        // json contained "error": "some string"
    }
    

    【讨论】:

      猜你喜欢
      • 2010-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多