Десериализация локальной NSString JSON в объекты через RestKit (без загрузки по сети) - PullRequest
15 голосов
/ 16 января 2012

Можно ли десериализовать NSString JSON в объекты через RestKit?Я проверил список API здесь и не смог найти то, что подойдет для этой цели.Ближе всего я могу найти различные классы синтаксического анализатора, которые возвращают NSDictionary после разбора ввода.Я предполагаю, что RestKit использует эти парсеры после загрузки ответа, так что я думаю, что эта функциональность доступна где-то в RestKit, но не доступна публично.

Если я ничего не пропустил, и эта функциональность не раскрыта, что будетальтернативы?Два очевидных из них не выглядят многообещающе: получите получившийся NSDictionary и попытайтесь десериализовать себя (эффективно реализуя RestKit) или попробуйте погрузиться в исходный код RestKit и посмотреть, можно ли это каким-то образом разоблачить (выглядит утомительно и подвержено ошибкам).

Заранее благодарим за любую помощь.

PS: Идея состоит в том, что строковое свойство десериализованного объекта на самом деле представляет собой JSON-представление другого набора объектов (в некотором смысле, встроенного JSON), и этодесериализуется по требованию во время выполнения.

Ответы [ 9 ]

9 голосов
/ 31 января 2012

Довольно "просто":

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]);
8 голосов
/ 16 февраля 2013

По состоянию на 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);
}
5 голосов
/ 02 декабря 2013

Это работает для Restkit 0.20, используя Core Data Entities . Он основан на решении, заданном @ 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 голосов
/ 28 ноября 2013

Это работает для 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]);
}
3 голосов
/ 07 февраля 2014

Вы можете увидеть, как 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];
}
1 голос
/ 26 января 2014

Для 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;
}
1 голос
/ 23 августа 2012

Более ориентированный на 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);
}
0 голосов
/ 22 января 2012

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

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

0 голосов
/ 16 января 2012

Разве это не то, что вы ищете?http://restkit.org/api/0.10.0/Classes/RKJSONParserJSONKit.html

...