使用 Objective C 解析 JSON 的弹性

Posted

技术标签:

【中文标题】使用 Objective C 解析 JSON 的弹性【英文标题】:Resilience in JSON parsing with ObjectiveC 【发布时间】:2015-11-25 08:52:25 【问题描述】:

我们的 ios ObjectiveC 应用使用的 JSON API 提要有点不稳定,因此有时字段为空。

我们在解析 JSON 时使用

NSDictionary *json = [self JSONFromResponseObject:responseObject];

然后尝试使用带有例如的字段

[widgetIDArray addObject:widget[@"name"][@"id"]];

有时“名称”字段为空。我们:

1) 要求 API 提供者清理其易碎的 API 代码

2) 每次我们尝试使用 json dict 中的内容时检查 null

if ( ![widget[@"name"] isKindOfClass:[NSNull class]] )

3) 使用 try-catch

 @try 
      [widgetIDArray addObject:widget[@"name"][@"id"]];
     
 @catch (NSException *exception) 
    
     NSLog(@"Exception %@",exception);
   

回答:

感谢您的回答,如下所示。这是我添加的 NSObject 扩展,它允许我获取可能存在或不存在的深度嵌套的 JSON 项。

第一次调用类似

self.item_logo = [self valueFromJSONWithKeyArray:event withKeyArray:@[@"categories",@"bikes",@"wheels",@"model",@"badge_uri"]];

这里是NSObject+extensions.m中的代码

- (id) valueFromJSONWithKeyArray:(id)json withKeyArray:(NSArray *)keyArray

    for (NSString * keyString in keyArray)
    
        if ([json[keyString] isKindOfClass:[NSObject class]])
        
            json = json[keyString]; // go down a level
        
        else
        
            return nil; // we didn't find this key
        
      
    return json; // We successfully found all the keys, return the object

【问题讨论】:

【参考方案1】:

JSON 响应中的 null 不是“不稳定的”,它绝对是标准的。

即使它“不稳定”,您从外部收到的任何消息都是攻击媒介,可能允许攻击者侵入您的程序,因此需要恢复能力。当您收到 null 时崩溃允许对您的应用程序进行 DOS 攻击。

@try / @catch 很糟糕。为响应编程错误而引发异常。你没有抓住他们,你修复了你的代码。

您如何修复您的代码?简单的。在 NSDictionary 扩展中编写一些辅助方法。

首先你不知道 json 是一个字典。因此,您添加一个 NSDictionary 类方法,您可以在其中传递任何内容,如果它是字典,则返回您传递的内容,如果是其他任何内容,则返回 nil(带有适当的日志记录)。

接下来,您假设在“名称”键下有一个字典。因此,您编写了一个扩展名“jsonDictionaryForKey”,如果有则返回字典,如果有则返回 nil(带有适当的日志记录)。

等等。如果您想称自己为专业开发人员,请制作您的 JSON 解析防弹。对于额外的奖励积分,您添加一个方法,该方法将获取字典并列出您没有要求的所有存在的键 - 这样您就知道您的 API 是否正在发送您不期望的东西。

【讨论】:

谢谢 gnash,我用 valueFromDict:(NSString*) 键助手扩展了我的 NSDictionary。如果为 NULL 或对象,则返回 false。 所以后面如果有数字而不是字符串会崩溃?如果它应该只返回字符串,为什么称它为 valueFromDict 而不是 stringFromDict?如果它在 NSDictionary 的扩展中,为什么是“FromDict”?没有必要说“FromDict”,尤其是“FromDict”后跟一个键?【参考方案2】:

您可以删除 JSON 对象中的所有 NSNULL 值。 Here 是我在库中使用的一个函数,用于删除 JSON 对象中的所有空值。

id BWJSONObjectByRemovingKeysWithNullValues(id json, NSJSONReadingOptions options) 
    if ([json isKindOfClass:[NSArray class]]) 
        NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)json count]];
        for (id value in (NSArray *)json) 
            [mutableArray addObject:BWJSONObjectByRemovingKeysWithNullValues(value, options)];
        

        return (options & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
     else if ([json isKindOfClass:[NSDictionary class]]) 
        NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:json];
        for (id<NSCopying> key in [(NSDictionary *)json allKeys]) 
            id value = [(NSDictionary *)json objectForKey:key];

            if (isNullValue(value)) 
                [mutableDictionary removeObjectForKey:key];
             else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) 
                [mutableDictionary setObject:BWJSONObjectByRemovingKeysWithNullValues(value, options) forKey:key];
            
        

        return (options & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
    

    return json;

在所有空值都被清除之后,也许异常也会消失。

【讨论】:

【参考方案3】:

一种经常使用的方法是类似于NSDictionaryNSMutableDictionary 上的类别,忽略nilNSNull

@interface NSDictionary (nullnilsafe)
- (ObjectType)nullnilsafe_objectForKey:(KeyType)aKey;
@end    

@implementation NSDictionary

- (ObjectType)nullnilsafe_objectForKey:(KeyType)aKey

    id obj = [self objectForKey:aKey];
    if (obj && ![obj isKindOfClass:[NSNull class]] )
        return obj;
    
    return nil; 


@end

@interface NSMutalbeDictionary (nullnilsafe)
- (void)nullnilsafe_setObject:(ObjectType)anObject forKey:(id<NSCopying>)aKey;
@end    

@implementation NSMutalbeDictionary
- (void)nullnilsafe_setObject:(ObjectType)anObject forKey:(id<NSCopying>)aKey

    if (anObject && aKey && ![anObject isKindOfClass:[NSNull class]])
        [self setObject:anObject forKey:aKey];
    


@end

【讨论】:

以上是关于使用 Objective C 解析 JSON 的弹性的主要内容,如果未能解决你的问题,请参考以下文章

在 Objective C 中使用 RestKit 解析复杂的 JSON

如何在 Objective C 中解析 JSON 格式

在 Objective C (iOS) 中解析 JSON

Objective C TableView清除Json解析

iOS / Objective C解析JSON获取int

将 JSON 字符串解析为对象数组 Objective C