Дезаминирование локальной NSString JSON на объекты через RestKit (без загрузки сети)
Можно ли десериализовать NSString
JSON на объекты через RestKit? Я проверил список API здесь и не смог найти то, что будет служить для этой цели. Ближе всего я могу найти различные классы парсеров, которые возвращают NSDictionary
после разбора ввода. Я предполагаю, что RestKit использует эти синтаксические анализаторы после загрузки ответа, поэтому я считаю, что функциональность доступна где-то в RestKit, но не публично публиковалась.
Если я ничего не пропущу, и эта функциональность не раскрывается, какими будут альтернативы? Два очевидных не выглядят очень многообещающе: получите результирующий NSDictionary
и попытайтесь десериализовать себя (эффективно переопределить RestKit) или попытаться погрузиться в источник RestKit и посмотреть, может ли это быть каким-то образом (выглядит утомительно и подвержено ошибкам).
Заранее благодарим за помощь.
PS: Идея состоит в том, что свойство string на десериализованном объекте является фактически представлением 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);
}
Ответ 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
Вы можете видеть, как RestKit делает это внутри класса RKManagedObjectResponseMapperOperation
.
Существует три этапа этой операции.
Во-первых, нужно проанализировать строку 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;
}];
Теперь вам нужно запустить задачи, которые были помещены в созданную операциюQueue. Именно на этом этапе создаются соединения с существующими объектами NSManagedObject.
if ([operationQueue operationCount]) {
[operationQueue waitUntilAllOperationsAreFinished];
}
Ответ 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
Ответ 9
Судя по мнениям без каких-либо ответов, похоже, что этого средства еще нет в RestKit. Вместо того, чтобы тратить больше времени на то, чтобы выяснить, как сделать сопоставление, я написал свой собственный картограф, используя вывод парсера JsonKit, и удалил зависимость от RestKit (использовал встроенные классы для сетевой активности). Прямо сейчас мой картограф не является общим (он имеет несколько зависимостей от того, как выкладываются объекты и их имена в json), но он работает для целей проекта. Я мог бы вернуться позже и позже включить его в более общую библиотеку сопоставления объектов.
РЕДАКТИРОВАТЬ: Это был выбран ответ, потому что на этот ответ не было другого ответа (21 января 2012 г.). С тех пор я перестала работать на iOS и больше никогда не посещала этот вопрос. Теперь я выбираю Ludovic ответ из-за другого комментария пользователя и upvotes для этого ответа.