Désérialisation de NSString locale de JSON en objets via RestKit (pas de téléchargement réseau)

StackOverflow https://stackoverflow.com/questions/8873607

  •  28-10-2019
  •  | 
  •  

Question

Est-il possible de désérialiser un NSString de JSON en objets via RestKit? J'ai vérifié la liste des API ici et je n'ai pas trouvé quelque chose qui servirait à cet effet. Les plus proches que j'ai pu trouver sont les différentes classes d'analyseurs qui retournent NSDictionary après l'analyse de l'entrée. Je suppose que RestKit utilise ces analyseurs après avoir téléchargé la réponse, donc je pense que la fonctionnalité est disponible quelque part dans RestKit mais pas exposée publiquement.

Si rien ne me manque et que cette fonctionnalité n'est pas exposée, quelles seraient les alternatives? Deux évidents ne semblent pas très prometteurs: obtenez le NSDictionary résultant et essayez de me désérialiser (réimplémentant efficacement RestKit) ou essayez de plonger dans la source de RestKit et voir si cela peut être exposé (semble fastidieux et sujet aux erreurs).

Merci d'avance pour toute aide.

PS: L'idée est qu'une propriété de chaîne sur un objet désérialisé est en fait la représentation JSON d'un autre ensemble d'objets (JSON incorporé dans un sens) et elle est désérialisée à la demande pendant l'exécution.

Était-ce utile?

La solution

Assez "simple":

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]);

Autres conseils

À partir de 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);
}

Cela fonctionne pour 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]);
}

Cela fonctionne pour Restkit 0.20, en utilisant des Entités de données de base .Il est basé sur la solution donnée par @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]
}

Vous pouvez voir comment RestKit fait cela en interne dans la classe RKManagedObjectResponseMapperOperation.

Cette opération se déroule en trois étapes.

La première consiste à analyser la chaîne JSON en NSDictionary, NSArrays, etc. C'est la partie la plus simple.

id parsedData = [RKMIMETypeSerialization objectFromData:data
                                               MIMEType:RKMIMETypeJSON
                                                  error:error];

Ensuite, vous devez exécuter une opération de mappage pour convertir ces données en vos NSManagedObjects.C'est un peu plus compliqué.

__block NSError *blockError = nil;
__block RKMappingResult *mappingResult = nil;
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.maxConcurrentOperationCount = 1;

[[RKObjectManager sharedManager].managedObjectStore.persistentStoreManagedObjectContext performBlockAndWait:^{

N'oubliez pas de remplacer ce dictionnaire par vos propres mappages.La clé [NSNull null] mappe cet objet à partir de la racine.

    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;
}];

Vous devez maintenant exécuter les tâches qui ont été placées dans la file d'opération que nous avons créée.C'est à ce stade que les connexions aux NSManagedObjects existants sont établies.

if ([operationQueue operationCount]) {
    [operationQueue waitUntilAllOperationsAreFinished];
}

Une réponse plus orientée 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);
}

Pour Restkit 0.22, vous pouvez utiliser ce code.Cela renvoie un RKMappingResult dans lequel vous pouvez énumérer les objets après le mappage en utilisant la propriété .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;
}

À en juger par les vues sans réponse, il semble que cette fonctionnalité n'existe pas encore dans RestKit.Au lieu de passer plus de temps à essayer de comprendre comment faire le mappage, j'ai écrit mon propre mappeur en utilisant la sortie de l'analyseur JsonKit et j'ai supprimé la dépendance à RestKit (utilisé les classes intégrées pour l'activité réseau).À l'heure actuelle, mon mappeur n'est pas générique (il a quelques dépendances sur la façon dont les objets sont disposés et leurs noms en json) mais il fonctionne pour les besoins du projet.Je pourrais revenir plus tard et en faire une bibliothèque de mappage d'objets plus générique plus tard.

MODIFIER: Cette réponse a été sélectionnée car il n'y avait pas d'autre réponse à la date de cette réponse (21 janvier 2012).Depuis, j'ai arrêté de travailler sur iOS et je n'ai plus jamais revu cette question.Maintenant, je sélectionne la réponse de Ludovic en raison du commentaire d'un autre utilisateur et des votes positifs pour cette réponse.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top