Deserializzazione della NSString locale di JSON in oggetti tramite RestKit (nessun download dalla rete)
Domanda
È possibile deserializzare un file NSString
di JSON in oggetti tramite RestKit?Ho controllato l'elenco delle API Qui e non sono riuscito a trovare qualcosa che servisse a questo scopo.Il più vicino che ho trovato sono le varie classi parser che restituiscono NSDictionary
dopo aver analizzato l'input.Presumo che RestKit utilizzi questi parser dopo aver scaricato la risposta, quindi penso che la funzionalità sia disponibile da qualche parte in RestKit ma non esposta pubblicamente.
Se non mi manca nulla e questa funzionalità non è esposta, quali sarebbero le alternative?Due ovvi non sembrano molto promettenti:Ottieni il risultato NSDictionary
e prova a deserializzarmi (reimplementando efficacemente RestKit) o prova ad immergerti nel sorgente RestKit e vedere se questo può essere in qualche modo esposto (sembra noioso e soggetto a errori).
Grazie in anticipo per qualsiasi aiuto.
PS:L'idea è che una proprietà stringa su un oggetto deserializzato sia in realtà la rappresentazione JSON di un altro insieme di oggetti (JSON incorporato in un certo senso) e venga deserializzata su richiesta durante il runtime.
Soluzione
Abbastanza semplice":
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]);
Altri suggerimenti
Come di 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);
}
Funziona con 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]);
}
Funziona con Restkit 0.20, utilizzando Entità di dati principali.Si basa sulla soluzione fornita da @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]
}
Puoi vedere come RestKit lo fa internamente nel file RKManagedObjectResponseMapperOperation
classe.
Ci sono tre fasi di questa operazione.
Il primo è analizzare la stringa JSON in NSDictionarys, NSArrays, ecc.Questa è la parte più semplice.
id parsedData = [RKMIMETypeSerialization objectFromData:data
MIMEType:RKMIMETypeJSON
error:error];
Successivamente è necessario eseguire un'operazione di mappatura per convertire questi dati nei tuoi NSManagedObjects.Questo è un po' più complicato.
__block NSError *blockError = nil;
__block RKMappingResult *mappingResult = nil;
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.maxConcurrentOperationCount = 1;
[[RKObjectManager sharedManager].managedObjectStore.persistentStoreManagedObjectContext performBlockAndWait:^{
Ricordati di sostituire questo dizionario con le tue mappature.Il tasto [NSNull null]
mappa questo oggetto dalla radice.
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;
}];
Ora devi eseguire le attività che sono state inserite nell'operazioneQueue che abbiamo creato.È in questa fase che vengono effettuate le connessioni agli NSManagedObject esistenti.
if ([operationQueue operationCount]) {
[operationQueue waitUntilAllOperationsAreFinished];
}
Una risposta più orientata a 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);
}
Per Restkit 0.22, puoi utilizzare questo codice.Ciò restituisce un RKMappingResult in cui è possibile enumerare gli oggetti dopo la mappatura utilizzando la proprietà .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;
}
Non è questo quello che stai cercando? http://restkit.org/api/0.10.0/Classes/RKJSONParserJSONKit.html
A giudicare dalle opinioni senza risposta, sembra che questa funzionalità non esista ancora in RestKit.Invece di dedicare più tempo a cercare di capire come eseguire la mappatura, ho scritto il mio mappatore utilizzando l'output del parser JsonKit e rimosso la dipendenza da RestKit (utilizzando le classi integrate per l'attività di rete).In questo momento il mio mappatore non è generico (ha alcune dipendenze da come sono disposti gli oggetti e dai loro nomi in json) ma funziona per gli scopi del progetto.Potrei tornare più tardi e trasformarlo in una libreria di mappatura di oggetti più generica in seguito.
MODIFICARE:Questa risposta è stata selezionata perché non c'erano altre risposte alla data di questa risposta (21 gennaio 2012).Da allora, ho smesso di lavorare su iOS e non ho mai più posto questa domanda.Ora seleziono la risposta di Ludovic a causa del commento di un altro utente e dei voti positivi per quella risposta.