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 types
、missing keys
、error 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