通过 RestKit 将 JSON 的本地 NSString 反序列化为对象(无网络下载)
Posted
技术标签:
【中文标题】通过 RestKit 将 JSON 的本地 NSString 反序列化为对象(无网络下载)【英文标题】:Deserializing local NSString of JSON into objects via RestKit (no network download) 【发布时间】:2012-01-15 21:42:47 【问题描述】:是否可以通过 RestKit 将 JSON 的 NSString
反序列化为对象?我检查了 API 列表 here 并找不到可以用于此目的的东西。我能找到的最接近的是在解析输入后返回NSDictionary
的各种解析器类。我假设 RestKit 在下载响应后使用这些解析器,所以我的想法是该功能在 RestKit 中的某个地方可用,但没有公开。
如果我没有遗漏任何东西并且没有公开此功能,还有什么替代方案?两个明显的看起来不太有希望:获取结果 NSDictionary
并尝试反序列化自己(有效地重新实现 RestKit)或尝试深入研究 RestKit 源代码,看看是否可以以某种方式暴露(看起来乏味且容易出错)。
提前感谢您的帮助。
PS:这个想法是反序列化对象上的字符串属性实际上是另一组对象的 JSON 表示(在某种意义上是嵌入的 JSON),并且在运行时按需反序列化。
【问题讨论】:
【参考方案1】:相当“简单”:
NSString *stringJSON;
...
RKJSONParserJSONKit *parser;
NSError *error= nil;
parser= [[[RKJSONParserJSONKit alloc] init] autorelease];
MyManagedObject *target;
target= [MyManagedObject object];
NSDictionary *objectAsDictionary;
RKObjectMapper* mapper;
objectAsDictionary= [parser objectFromString:stringJSON error:&error];
mapper = [RKObjectMapper mapperWithObject:objectAsDictionary
mappingProvider:[RKObjectManager sharedManager].mappingProvider];
mapper.targetObject = target;
RKObjectMappingResult* result = [mapper performMapping];
NSLog(@"%@", [result asObject]);
【讨论】:
【参考方案2】:截至RestKit 0.20.0-pre2
NSString* JSONString = @" \"name\": \"The name\", \"number\": 12345";
NSString* MIMEType = @"application/json";
NSError* error;
NSData *data = [JSONString dataUsingEncoding:NSUTF8StringEncoding];
id parsedData = [RKMIMETypeSerialization objectFromData:data MIMEType:MIMEType error:&error];
if (parsedData == nil && error)
// Parser error...
AppUser *appUser = [[AppUser alloc] init];
NSDictionary *mappingsDictionary = @ @"someKeyPath": someMapping ;
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData mappingsDictionary:mappingsDictionary];
mapper.targetObject = appUser;
NSError *mappingError = nil;
BOOL isMapped = [mapper execute:&mappingError];
if (isMapped && !mappingError)
// Yay! Mapping finished successfully
NSLog(@"mapper: %@", [mapper representation]);
NSLog(@"firstname is %@", appUser.firstName);
【讨论】:
您知道如何从已配置的 RKObjectManager 中执行此操作吗? 这是您要找的吗? github.com/RestKit/…【参考方案3】:这适用于 Restkit 0.21.0:
NSString* jsonFilePath = [[NSBundle mainBundle] pathForResource:@"fileName"
ofType:@"json"];
NSString* JSONString = [NSString stringWithContentsOfFile:jsonFilePath
encoding:NSUTF8StringEncoding
error:NULL];
NSError* error;
NSData *data = [JSONString dataUsingEncoding:NSUTF8StringEncoding];
id parsedData = [RKMIMETypeSerialization objectFromData:data MIMEType:RKMIMETypeJSON error:&error];
if (parsedData == nil && error)
// Parser error...
//_objectManager is RKObjectManager instance
NSMutableDictionary *mappingsDictionary = [[NSMutableDictionary alloc] init];
for (RKResponseDescriptor *descriptor in _objectManager.responseDescriptors)
[mappingsDictionary setObject:descriptor.mapping forKey:descriptor.keyPath];
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData mappingsDictionary:mappingsDictionary];
NSError *mappingError = nil;
BOOL isMapped = [mapper execute:&mappingError];
if (isMapped && !mappingError)
NSLog(@"result %@",[mapper mappingResult]);
【讨论】:
【参考方案4】:这适用于 Restkit 0.20,使用 核心数据实体。它基于@innerself 给出的解决方案
NSString* jsonFilePath = [[NSBundle mainBundle] pathForResource:@"info-base"
ofType:@"json"];
NSString* JSONString = [NSString stringWithContentsOfFile:jsonFilePath
encoding:NSUTF8StringEncoding
error:NULL];
NSError *error = nil;
NSData *data = [JSONString dataUsingEncoding:NSUTF8StringEncoding];
id parsedData = [RKMIMETypeSerialization objectFromData:data MIMEType:RKMIMETypeJSON error:&error];
if (parsedData == nil && error)
// Parser error...
NSLog(@"parse error");
//_objectManager is RKObjectManager instance
NSMutableDictionary *mappingsDictionary = [[NSMutableDictionary alloc] init];
for (RKResponseDescriptor *descriptor in [RKObjectManager sharedManager].responseDescriptors)
[mappingsDictionary setObject:descriptor.mapping forKey:descriptor.keyPath];
RKManagedObjectMappingOperationDataSource *datasource = [[RKManagedObjectMappingOperationDataSource alloc]
initWithManagedObjectContext:[RKManagedObjectStore defaultStore].persistentStoreManagedObjectContext
cache:[RKManagedObjectStore defaultStore].managedObjectCache];
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData
mappingsDictionary:mappingsDictionary];
[mapper setMappingOperationDataSource:datasource];
NSError *mappingError = nil;
BOOL isMapped = [mapper execute:&mappingError];
if (isMapped && !mappingError)
// data is in [mapper mappingResult]
【讨论】:
【参考方案5】:您可以在 RKManagedObjectResponseMapperOperation
类中看到 RestKit 是如何在内部执行此操作的。
此操作分为三个阶段。
首先是将JSON字符串解析成NSDictionarys、NSArrays等,这是最简单的部分。
id parsedData = [RKMIMETypeSerialization objectFromData:data
MIMEType:RKMIMETypeJSON
error:error];
接下来,您需要运行映射操作来将此数据转换为您的 NSManagedObjects。这个有点牵强。
__block NSError *blockError = nil;
__block RKMappingResult *mappingResult = nil;
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.maxConcurrentOperationCount = 1;
[[RKObjectManager sharedManager].managedObjectStore.persistentStoreManagedObjectContext performBlockAndWait:^
记得用你自己的映射替换这个字典。键 [NSNull null]
从根映射此对象。
NSDictionary *mappings = @[NSNull null]: [jotOfflineRequestStatus mapping];
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData
mappingsDictionary:mappings];
RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc]
initWithManagedObjectContext:[RKManagedObjectStore defaultStore].persistentStoreManagedObjectContext
cache:[RKManagedObjectStore defaultStore].managedObjectCache];
dataSource.operationQueue = operationQueue;
dataSource.parentOperation = mapper;
mapper.mappingOperationDataSource = dataSource;
[mapper start];
blockError = mapper.error;
mappingResult = mapper.mappingResult;
];
您现在需要运行已放入我们创建的 operationQueue 的任务。正是在这个阶段建立了与现有 NSManagedObjects 的连接。
if ([operationQueue operationCount])
[operationQueue waitUntilAllOperationsAreFinished];
【讨论】:
+1!我实现的关键是您的最后一步, operationQueue.waitUntilAllOperationsAreFinished()。没有它,映射过程在完成映射所需关系之前返回我的 NSManagedObject。 这对我有用。你如何让 RestKit 将对象存储回 Core Data?我试过 [[RKObjectManager sharedManager].managedObjectStore.persistentStoreManagedObjectContext saveToPersistentStore:&error];在mappingResult = mapper.mappingResult之后;但这没有帮助。有什么建议吗?【参考方案6】:一个更面向 ios 5+ 的答案:
NSString* JSONString = jsonString;
NSString* MIMEType = @"application/json";
NSError* error = nil;
id<RKParser> parser = [[RKParserRegistry sharedRegistry] parserForMIMEType:MIMEType];
id parsedData = [parser objectFromString:JSONString error:&error];
if (parsedData == nil && error)
NSLog(@"ERROR: JSON parsing error");
RKObjectMappingProvider* mappingProvider = [RKObjectManager sharedManager].mappingProvider;
RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:parsedData mappingProvider:mappingProvider];
RKObjectMappingResult* result = [mapper performMapping];
if (result)
NSArray *resultArray = result.asCollection;
MyObject *object = [resultArray lastObject];
NSLog(@"My Object: %@", object);
【讨论】:
【参考方案7】:对于 Restkit 0.22,您可以使用此代码。这将返回一个 RKMappingResult,您可以在其中使用属性 .array 进行映射后枚举对象。
- (RKMappingResult *)mapJSONStringWithString:(NSString *)jsonString
RKMappingResult *result = nil;
NSError* error;
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
id parsedData = [RKMIMETypeSerialization objectFromData:data MIMEType:RKMIMETypeJSON error:&error];
if (parsedData == nil && error)
NSLog(@"json mapping error");
NSDictionary *mappingsDictionary = @@"":[CustomMappingClass getMappingForUsers];
ObjectClass *obj = [ObjectClass new];
RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData mappingsDictionary:mappingsDictionary];
NSError *mappingError = nil;
mapper.targetObject = obj;
BOOL isMapped = [mapper execute:&mappingError];
if (isMapped && !mappingError)
result = [mapper mappingResult];
return result;
【讨论】:
【参考方案8】:这不是您要找的吗? http://restkit.org/api/0.10.0/Classes/RKJSONParserJSONKit.html
【讨论】:
我知道这些解析器类。他们将字符串解析为NSDictionary
,这是整个反序列化过程的第一部分。我需要接受字符串并输出映射对象的功能。换句话说,在 RestKit 依次完成的三件事中(下载响应、解析下载的字符串、映射对象),我需要只做最后两件事的事情。
嘿,我也遇到了同样的问题。我有一些 JSON 需要映射到我的模型(NSManagedObject)。你找到解决办法了吗?
@MikeBevz:我编写了自己的映射器。 :-) 我写了更多细节来回答这个问题。【参考方案9】:
从没有任何答案的观点来看,RestKit 中似乎还没有这个工具。我没有花更多时间试图弄清楚如何进行映射,而是使用 JsonKit 解析器的输出编写了自己的映射器,并删除了对 RestKit 的依赖(使用内置类进行网络活动)。现在我的映射器不是通用的(它对对象的布局方式和它们在 json 中的名称有一些依赖关系)但它适用于项目的目的。稍后我可能会回来并将其转换为更通用的对象映射库。
编辑:这是选择的答案,因为截至该答案的日期(2012 年 1 月 21 日)没有其他答案。从那以后,我停止了在 iOS 上的工作,并且再也没有访问过这个问题。现在我选择 Ludovic 的答案是因为另一个用户的评论和对该答案的支持。
【讨论】:
为什么选择这个答案? Ludovic 的解决方案效果很好! @Kyle:这是选择的答案,因为截至该答案的日期(2012 年 1 月 21 日)没有其他答案。从那以后,我停止了在 iOS 上的工作,并且再也没有访问过这个问题。尽管我目前无法亲自验证答案,但由于您的评论,我选择了 Ludovic 的答案。以上是关于通过 RestKit 将 JSON 的本地 NSString 反序列化为对象(无网络下载)的主要内容,如果未能解决你的问题,请参考以下文章
RestKit 无法将 JSON 请求中格式为字符串的日期保存到我的本地数据库
如何使用带有 RKObjectMapping 的 RestKit 0.24 将本地 JSON 字符串映射到对象?
iOS RestKit 0.2 解析本地 Json 的最佳方法是啥
RestKit 0.20.1 映射本地 JSON “此类不符合键值编码...”