从 JSON 到 NSObjects 的对象映射库
Posted
技术标签:
【中文标题】从 JSON 到 NSObjects 的对象映射库【英文标题】:Object Mapping library from JSON to NSObjects 【发布时间】:2011-05-26 10:22:13 【问题描述】:我正在尝试构建一个解析器/objectMapper,它将为我从 REST 服务使用的 JSON 构建 Objective C 对象。
我从 RestKit 中获得了一些灵感,让我的实体都拥有一个“解码列表”,它告诉映射器哪些 JSON 键与哪些对象对应。像这样:
//ObjectEntity implementation
+ (NSDictionary*) mapProperties
/*
localPropertiy - JSONProperty
*/
return @
@"name": @"name",
@"category": @"category",
@"possible_scopes": @"possibleScopes",
@"possible_descriptions": @"possibleDescriptions",
@"key": @"keys"
;
+ (NSDictionary*) mapRelations
return [NSDictionary dictionary];
我这样做是因为我喜欢将这些可变值封装在它们引用的对象中。让 Mapper 知道的越少越好。
映射器做这样的事情:
+ (NSArray*) parseData:(NSData*) jsonData intoObjectsOfType:(Class) objectClass
//Parser result from web service
NSError *error = nil;
CJSONDeserializer *deserializer = [CJSONDeserializer deserializer];
[deserializer setNullObject:nil];
NSArray *objects = [deserializer deserializeAsArray:jsonData error:&error];
NSMutableArray *result = [NSMutableArray array];
for (NSDictionary *o in objects)
id <EntityProtocol> entity = [[objectClass alloc] init];
NSDictionary *jsonKeys = objectClass.mapProperties;
for (NSString *key in jsonKeys.allKeys)
NSString *objectProperty = jsonKeys[key];
NSString *value = o[key];
if (value)
[entity setValue:value forKey:objectProperty];
[result addObject:entity];
return (NSArray*)result;
所以我像这样向解析器/映射器发送消息:
NSArray *objects = [ObjectParser parseData:self.responseData intoObjectsOfType:ObjectEntity.class];
这意味着解析器必须知道我的根对象是什么,这很好,因为从 Web 服务中检索它的对象当然有这个知识。
以上仅适用于没有嵌套对象的 JSON,我一直在尝试构建解析器,以便它也会考虑关系,构建所需的对象并将它们插入到根对象中,这需要递归我一直在死胡同。
我想要一些帮助来了解如何处理这个问题或任何洞察力,就好像这样的东西作为一个库存在。也许是为了使用,或者只是为了解决我遇到问题的部分。
提前谢谢你。
【问题讨论】:
+1 因为你做的很酷! 谢谢亚历克,这些天似乎很难弄清楚很酷的事情。 我们有一个相当复杂的设置来发送/接收 JSON,但它的要点是所有启用 JSON 的类都有一个initWithDict
方法和一个 asDictionary
方法。每个类负责读取/写入它“拥有”的数据,并依次为组件类调用相同的方法。
【参考方案1】:
考虑使用 RestKit:http://restkit.org
这个框架拥有你所需要的一切——REST 和 JSON 抽象、对象映射,甚至是核心数据支持和许多真正有用的东西,所有这些都以可定制和优雅的方式实现。
更新:好的,在编写另一种映射方法时,我决定我不能再这样做了,并做了一个小框架。它内省对象的属性,并通过一些调整为您提供免费的漂亮描述、isEqual/hashCode、免费的 NSCoding 支持,并允许生成到/从 JSON 映射器(好的,实际上是 NSDictionary,但谁会将它用于其他用途)。所有 NSNull 检查、JSON 中的缺失字段、JSON 中的新意外字段都得到妥善处理并正确报告。
如果有人希望将此分享给公众,您可以给我点赞或 cmets。我最终会这样做,但我可能会考虑更快地分享它。
【讨论】:
已经用了半年了:)是的,强烈推荐! 我发现它很难安装。而在 ASIHTTPRequests 停止支持后,我更不愿意依赖更大的框架。【参考方案2】:为什么不为类添加映射?
+ (NSDictionary *)mapClasses
return @
@"category": [Category class],
// ...
;
对于非容器属性,您甚至可以使用runtime introspection of properties 来避免冗余映射。
容器属性可以映射到特殊的包装对象:
[OMContainer arrayWithClass:Key.class], @"keys",
[OMContainer dicionaryWithKeyClass:ScopeID.class valueClass:Scope.class], @"possibleScopes",
甚至是动态选择类型的块:
[OMDynamicType typeWithBlock:^(id obj)
if ([obj isKindOfClass:NSString.class] && [obj hasPrefix:@"foo"])
return Foo.class;
else
return Bar.class;
], @"foo",
实现这一点可能是这样的:
+ (NSArray *)parseData:(NSData*)jsonData intoObjectsOfType:(Class)objectClass
NSArray *parsed = /* ... */
NSMutableArray *decoded = [NSMutableArray array];
for (id obj in parsed)
[decoded addObject:[self decodeRawObject:parsed intoObjectOfType:objectClass]];
return decoded;
+ (id)decodeRawObject:(NSDictionary *)dict intoObjectOfType:(Class)objectClass
// ...
NSDictionary *jsonKeys = objectClass.mapProperties;
NSDictionary *jsonClasses = objectClass.mapClasses;
for (NSString *key in jsonKeys.allKeys)
NSString *objectProperty = jsonKeys[key];
NSString *value = dict[key];
if (value)
id klass = jsonClasses[key];
if (!klass)
[entity setValue:value forKey:objectProperty];
else if (klass == klass.class)
[entity setValue:[self decodeRawObject:value intoObjectOfType:klass]
forKey:objectProperty];
else if (/* check for containers and blocks */)
// ...
// ...
【讨论】:
谢谢威尔伯!非常好的东西,足以让我前进。我喜欢你在字典中返回一个类的想法,这样可以很容易地进行自省。【参考方案3】:我也尝试了同样的方法。我的主要问题是,准确地描述类型。例如对于嵌套数组,我使用了这样的东西:
@ @"friends" : @[[Person class]]
但是事情变得一团糟,因为我需要为不同的类型发明越来越多的特殊语法,我想支持这些语法。最后,我停止了这种方法,转而寻求另一种解决方案 - 也是因为我需要做的运行时检查会减慢转换速度。
现在有很多解决方案。我建议看看Awesome ios 网站。我还想指出JSON Class Generator,因为它可以让你
准确描述您的类型(包括日期等特殊映射器), 自动检查类型(报告不匹配并提供后备值), 您无需花时间编写重复代码(而且生成的代码似乎总是“正常工作”)我讨厌做广告,但这个工具为我节省了很多工作,我投资于我的应用的其他功能。它出现的有点晚了,现在世界似乎转向了 Swift,但更有理由不浪费时间在 Objective-C 中编写模型。
以下是一些转换为/形成 JSON 的代码示例:
// converting MyClass <-> NSDictionary
MyClass *myClass = [MyClass myClassWithDict:someJsonDictionary];
NSDictionary *jsonDict = [myClass toDict];
// converting NSDictionary <-> NSData
NSDictionary *someJsonDictionary = [NSDictionary api_dictionaryWithJson:someJsonData];
NSData *jsonData = [someJsonDictionary api_toJson];
JSON Class Generator也支持上一条评论中提到的功能:
-isEqual, -hash, -description, -copy, NSCoding/NSSecureCoding/NSKeyedArchiver
免费支持,并检查 JSON 响应中的缺失/附加/NSNull/可选字段和错误类型,报告错误,优雅地失败并返回后备值,并使用方法分派而不是运行时类型自省来执行此操作(更快) .
【讨论】:
【参考方案4】:试试 CSMapper,太棒了!您只需创建与类命名相同的 plist,映射一些属性,然后您可以使用一行代码轻松地将字典映射到您的对象。我已经在许多项目上对其进行了测试,发现它非常干净、高效。它使我能够灵活地在开发生命周期中轻松响应 API 更改。
https://github.com/marcammann/CSMapper
我目前正在更新文档并在个人分支上添加到项目中,希望尽快合并:)
https://github.com/AntonTheDev/CSMapper
【讨论】:
【参考方案5】:我的建议是在 NSObject 上使用 Motis 类别。它是轻量级的,对您的对象类型执行自动验证并且非常易于使用。
在另一个问题中有一个例子:Mapping JSON objects in custom objects
希望有用。
【讨论】:
以上是关于从 JSON 到 NSObjects 的对象映射库的主要内容,如果未能解决你的问题,请参考以下文章
Firebase Documentsnapshot 映射到 Flutter 中的 Json 对象
对包含 NSNumber 变量的 NSobjects 的 NSArray 进行排序
从 json 动态创建的 Javascript 表单输入,需要将对象映射到另一个对象的正确字段并保存