Дезаминирование локальной 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;
}

Ответ 9

Судя по мнениям без каких-либо ответов, похоже, что этого средства еще нет в RestKit. Вместо того, чтобы тратить больше времени на то, чтобы выяснить, как сделать сопоставление, я написал свой собственный картограф, используя вывод парсера JsonKit, и удалил зависимость от RestKit (использовал встроенные классы для сетевой активности). Прямо сейчас мой картограф не является общим (он имеет несколько зависимостей от того, как выкладываются объекты и их имена в json), но он работает для целей проекта. Я мог бы вернуться позже и позже включить его в более общую библиотеку сопоставления объектов.

РЕДАКТИРОВАТЬ: Это был выбран ответ, потому что на этот ответ не было другого ответа (21 января 2012 г.). С тех пор я перестала работать на iOS и больше никогда не посещала этот вопрос. Теперь я выбираю Ludovic ответ из-за другого комментария пользователя и upvotes для этого ответа.