Objective-c 中更好的 JSON 解析实现和最佳实践?

Posted

技术标签:

【中文标题】Objective-c 中更好的 JSON 解析实现和最佳实践?【英文标题】:Better JSON parsing implementation in Objective-c and best practices? 【发布时间】:2016-03-25 10:39:20 【问题描述】:

我收到以下 JSON 响应,用于在核心数据中保存用户偏好。

preferences=
      1=
        children=(
          3=Samsung;4=Nokia;
        );id=1;name=Mobiles;
      ;2=
        children=(
          5="Samsung Curve TV";
        );id=2;name=Electronics;
      ;
    ;

这是我的代码 sn-p 工作正常。但我认为这是非常冗长的代码。

    NSLog(@"Preferences: %@", [response objectForKey:@"preferences"]);

    for (NSDictionary *dic in [response objectForKey:@"preferences"]) 
        NSLog(@"ID: %@", [[[response objectForKey:@"preferences"] objectForKey:dic] objectForKey:@"id"]);
        NSLog(@"NAME: %@", [[[response objectForKey:@"preferences"] objectForKey:dic] objectForKey:@"name"]);

        NSLog(@"Children DIC: %@", [[[[[response objectForKey:@"preferences"]
                                     objectForKey:dic] objectForKey:@"children"] objectAtIndex:0] objectForKey:@"3"]);

        for (NSDictionary *childDic in [[[[response objectForKey:@"preferences"]
                                          objectForKey:dic] objectForKey:@"children"] objectAtIndex:0]) 
            NSLog(@"Child Name: %@", [[[[[response objectForKey:@"preferences"]
                                        objectForKey:dic] objectForKey:@"children"] objectAtIndex:0] objectForKey:childDic]);
        
    

我有 3 个问题。

    如何改进我的代码 sn-p?有没有更短的方法来实现这个?

    这个 JSON 响应是否足以用于移动端解析?它是好的 JSON 格式吗?作为移动开发人员,在使用 Core 数据方面,我们是否应该遵循任何 JSON 响应格式(这只是将 DB 实现减少为最佳实践)?

    如何从 Objective-c 再次构造这样的 JSON 字符串?

【问题讨论】:

了解其他人的表现,第三方例如github.com/icanzilb/JSONModel 你的问题有答案。 @Injectios 你有什么建议,我可以将上面的 JSON 响应转换为 JSONModel 吗?我知道 JSONModel 但我不知道这个响应如何与它兼容? @user3182143 对不起,我没有找到你?您能否建议显示标准 JSON 格式的适当且可靠的来源,因为我认为这不是好的 JSON 响应格式。 是的,您可以使用 JSONModel 转换任何类型的响应和嵌套元素,您需要注意的只有一件事是映射键(我会尝试编写示例) 【参考方案1】:

好吧,(抱歉没有深入分析)首先我会修改你的 JSON 不包含 dictionary 用于动态元素(三星有键 3 等等,它应该是数组,有意义吗?)

我想出了更好的 JSON 结构:

  
   "preferences":[
      
         "items":[
            
            "id" : "3",
            "name" : "Samsung" 
            ,
            
            "id" : "3",
            "name" : "Nokia" 
            
         ],
         "id":"1",
         "name":"Mobiles"
      ,
        
         "items":[
           
            "id" : "3",
            "name" : "Nokia" 
            
         ],
         "id":"2",
         "name":"Electronics"
      
]

现在使用JSONModel 将其映射到对象真的很容易,您应该关心的只有一件事是映射键。

 NSString *JSONString = @"            \"preferences\":[                    \"items\":[                            \"id\" : \"3\",                \"name\" : \"Samsung\"            ,                            \"id\" : \"3\",                \"name\" : \"Nokia\"                        ],            \"id\":\"1\",            \"name\":\"Mobiles\"        ,                    \"items\":[                            \"id\" : \"3\",                \"name\" : \"Nokia\"                        ],            \"id\":\"2\",            \"name\":\"Electronics\"                ]    ";


    NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:[JSONString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];

    NSError *mapError;
    GlobalPreferences *globalPreferences = [[GlobalPreferences alloc] initWithDictionary:dictionary error:&mapError];
    if (mapError) 
        NSLog(@"Something went wrong with mapping model %@", mapError);
    

    NSLog(@"Your mapped model: %@", globalPreferences);

型号:

Kinda GlobalPreference,根模型,以防你决定添加一些额外的东西

 #import <JSONModel/JSONModel.h>
    #import "Preference.h"

    @interface GlobalPreferences : JSONModel

    @property (nonatomic, strong) NSArray<Preference> *preferences; // using protocol here you specifying to which model data should be mapped
    @property (nonatomic, strong) NSArray<Optional> *somethingElse; // some other settings may be here

    @end

    #import "GlobalPreferences.h"

    @implementation GlobalPreferences

    @end

偏好

#import <JSONModel/JSONModel.h>
  #import "PreferenceItem.h"

    @protocol Preference <NSObject>

    @end

    @interface Preference : JSONModel

    @property (nonatomic, strong) NSNumber *ID; // required
    @property (nonatomic, strong) NSString *name; // required
    @property (nonatomic, strong) NSArray<PreferenceItem> *items;

    @end

    #import "Preference.h"

    @implementation Preference

    #pragma mark - JSONModel

    + (JSONKeyMapper *)keyMapper 
        return [[JSONKeyMapper alloc] initWithDictionary:@
                                                           @"id": @"ID",
                                                           ];
    

    @end

首选项

    #import <JSONModel/JSONModel.h>

    // This protocol just to let JSONModel know to which model needs to be parsed data in case if it's an array/dictionary
    @protocol PreferenceItem <NSObject>
    @end

@interface PreferenceItem : JSONModel

@property (nonatomic, strong) NSNumber *ID;
@property (nonatomic, strong) NSString *name;

@end
#import "PreferenceItem.h"

@implementation PreferenceItem

#pragma mark - JSONModel

+ (JSONKeyMapper *)keyMapper 
    return [[JSONKeyMapper alloc] initWithDictionary:@
                                                       @"id": @"ID",
                                                       ];


@end

coreData 应该没问题。

也许对您而言,这一切都不是必需的,但是当您解析/映射网络响应时,您需要注意很多事情,例如 data typesmissing keyserror handling 等。如果你手动映射它 - 你有可能有一天会破坏应用程序。

【讨论】:

感谢您的详细解答。你的意思是没有 "items":[ "id" : "3", "name" : "Nokia" ] ,这种响应的键(例如:id)很难解析?我的后端人员认为他们是专家,不喜欢更改 JSON 格式。我仍然可以将他们的响应与 JSON 模型一起使用吗?我该如何向他们解释这一点? :D(即为什么要求任何可靠的消息来源来展示其他专家的真实情况) 我猜,只是说数据库中的值不应该是集合(字典等)的键,否则你如何将它映射到你的模型?您需要创建属性以通过这些键解析 json,对吗?您不知道值,而是知道数据库中的字段(id、名称等) 如果你使用他们的 JSON,你不能有你的自定义模型,你只需要使用 NSDictionary 作为一个模型对象,很简单,就像你已经做的一样。或者你需要手动映射它,比如 model.id = dictionary.key 等等。我想这不是做 API 的正确方法,检查一下 ***.com/questions/12806386/…【参考方案2】:

我知道你询问了 Objective-C 的最佳实践,但我建议切换到 Swift 并使用 SwiftyJSON。使用 Swift 的代码比使用 Objective-C 的代码更具可读性。

let prefs = json["preferences"]
let userName = prefs["id"].stringValue
let name = prefs["name"].stringValue
let child = prefs["children"].arrayValue[0].arrayValue[3]

https://github.com/SwiftyJSON/SwiftyJSON

您可以轻松构建自己的 JSON 结构并使用 Alamofire 发送它们:

let parameters: [String : AnyObject] = [
  "preferences": [
    "id": "123",
    "name": "John",
    "children": [
       ["3": "Three"]
    ]
  ]
]

let urlReq = NSMutableURLRequest(url)
urlReq.HTTPMethod = .POST

let req = Alamofire.ParameterEncoding.JSON.encode(urlReq, parameters: parameters).0
let request = Alamofire.request(req)

https://github.com/Alamofire/Alamofire

【讨论】:

感谢您的回答,但目前我能得到的最佳方法是什么?

以上是关于Objective-c 中更好的 JSON 解析实现和最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章

在 IOS(Objective-C)中有效地解析 JSON 到 Realm DB

在 Objective-C 中解析 JSON 后的随机结果

如何在 Objective-C iOS 中解析 JSON

在 Objective-C 中解析 JSON 的最漂亮方法?

在 Objective-C 中解析 JSON 数组

Json在objective-c中解析nil字段