JXMagicObject : mapping facile entre dictionnaires et properties
JegnuX
Membre
Salut à tous,
Bon j'ai encore passé mon temps libre à jouer avec le runtime, mais cette fois je pense que ce sera bien plus utile que mon JXOrientation /smile.png' class='bbc_emoticon' alt=':)' />
Voici JXMagicObject
Mais à quoi ça sert ?
Si comme moi vous avez souvent à faire à des webservices qui vous retourne du JSON, vous les convertissez sûrement en NSDictionnary (enfin, JSONKit le fait, pas vous /tongue.png' class='bbc_emoticon' alt=':P' />)
Le truc c'est qu'on est tous d'accord pour dire que les dico c'est un peu chiant à manipuler, et que c'est mieux d'avoir des vrais objets avec des properties.
Donc comme moi vous vous tapez surement l'ensemble du modèle de donnée pour faire une classe à chaque fois et implémenter des méthodes de stérilisation/désérialisations.
Mais ça peut devenir rapidement long et fastidieux. Pour peux que dans votre dico ce soit des @42 (donc string) et que vous souhaitez du NSInteger, bah faut se taper toutes les transformation.
Et bien JXMagicObject fait tout ça pour vous. C'est une classe abstraite qui fait de l'introspection sur les declared properties et se charge de faire le mapping avec le dictionnaire.
De plus le dictionnaire reste toujours disponible, ainsi si vous devez faire un PUT sur votre webservice, vous avez le JSON correspondant à votre objet en deux-deux. /smile.png' class='bbc_emoticon' alt=':)' />
Petit exemple d'une classe JXUser :
JXUser.h
JXUser.m
Et ça s'utilise comme ça :
Ce qui nous sort :
Plusieurs types et classes sont transformées automatiquement, pour le reste on peut passer par une sous classe de NSValueTransformer.
Je vous laisse voir la doc sur le github pour voir les petites subtilités (comme par exemple la différence entre un @dynamic et un @synthesize dans ce cas précis).
http://github.com/JegnuX/JXMagicObject
Bon j'ai encore passé mon temps libre à jouer avec le runtime, mais cette fois je pense que ce sera bien plus utile que mon JXOrientation /smile.png' class='bbc_emoticon' alt=':)' />
Voici JXMagicObject
Mais à quoi ça sert ?
Si comme moi vous avez souvent à faire à des webservices qui vous retourne du JSON, vous les convertissez sûrement en NSDictionnary (enfin, JSONKit le fait, pas vous /tongue.png' class='bbc_emoticon' alt=':P' />)
Le truc c'est qu'on est tous d'accord pour dire que les dico c'est un peu chiant à manipuler, et que c'est mieux d'avoir des vrais objets avec des properties.
Donc comme moi vous vous tapez surement l'ensemble du modèle de donnée pour faire une classe à chaque fois et implémenter des méthodes de stérilisation/désérialisations.
Mais ça peut devenir rapidement long et fastidieux. Pour peux que dans votre dico ce soit des @42 (donc string) et que vous souhaitez du NSInteger, bah faut se taper toutes les transformation.
Et bien JXMagicObject fait tout ça pour vous. C'est une classe abstraite qui fait de l'introspection sur les declared properties et se charge de faire le mapping avec le dictionnaire.
De plus le dictionnaire reste toujours disponible, ainsi si vous devez faire un PUT sur votre webservice, vous avez le JSON correspondant à votre objet en deux-deux. /smile.png' class='bbc_emoticon' alt=':)' />
Petit exemple d'une classe JXUser :
JXUser.h
<br />
@interface JXUser : JXMagicObject<br />
@property (nonatomic, retain) NSString *firstName;<br />
@property (nonatomic, retain) NSString *lastName;<br />
@property (nonatomic, retain) NSDate *birthdate;<br />
@property (nonatomic, assign) NSInteger favoriteNumber;<br />
@end<br />
JXUser.m
<br />
@implementation JXUser<br />
@synthesize firstName, lastName, birthdate;<br />
@dynamic favoriteNumber;<br />
// Setup mappings if :<br />
- (void) setupMappings<br />
{<br />
// keys are different<br />
[self mapPropertyKey:@"firstName" toDictionaryKey:@"name"];<br />
[self mapPropertyKey:@"favoriteNumber" toDictionaryKey:@"favorite_number"];<br />
// you need a specific transformation<br />
JXStringToDateValueTransformer *transformer = [[JXStringToDateValueTransformer alloc] init];<br />
<br />
[self mapPropertyKey:@"birthdate" toDictionaryKey:@"birth_date" usingValueTransformer:transformer];<br />
<br />
[transformer release];<br />
}<br />
- (void)dealloc<br />
{<br />
[firstName release];<br />
[lastName release];<br />
[birthdate release];<br />
[super dealloc];<br />
}<br />
@end<br />
Et ça s'utilise comme ça :
<br />
NSDictionary *userDict = [NSDictionary dictionaryWithObjectsAndKeys:<br />
@"Jerome", @"name",<br />
@"Alves", @"lastName",<br />
@"42", @"favorite_number",<br />
@"08/18/1989", @"birth_date", nil];<br />
JXUser *user = [[JXUser alloc] initWithDictionary:userDict];<br />
<br />
NSLog(@"%@ %@", user.firstName, user.lastName);<br />
user.favoriteNumber = 1337;<br />
user.birthDate = [NSDate date];<br />
<br />
NSLog(@"Dictionary : %@", [user dictionary]);<br />
<br />
[user release];<br />
Ce qui nous sort :
Jerome Alves
Dictionary : {
"birth_date" = "07/28/2012";
"favorite_number" = 1337;
lastName = Alves;
name = "Jerome";
}
Plusieurs types et classes sont transformées automatiquement, pour le reste on peut passer par une sous classe de NSValueTransformer.
Je vous laisse voir la doc sur le github pour voir les petites subtilités (comme par exemple la différence entre un @dynamic et un @synthesize dans ce cas précis).
http://github.com/JegnuX/JXMagicObject
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Patience.
Mais juste deux questions :
Quand on doit faire un mapping special, est-on obligé de redéfinir le mapping pour toutes les propriétés ?
Ne pourrait-on pas carrément se passer d'avoir à définir la classe de l'objet magique en utilisant des méthodes créées dynamiquement ?
http://developer.app...08048-CH102-SW1
http://stackoverflow.com/questions/7185122/dynamic-method-creation-in-objective-c
Non, par défaut si une property s'appelle name, alors dans le dictionnaire ça sera mappé à la key "name".
Donc on a besoin de faire le "mapPropertyKey: toDictionaryKey:" que pour les property dont on veut une key différente.
Je comprend pas la question /tongue.png' class='bbc_emoticon' alt=':P' />
Y'a déjà une distinction entre le dynamic properties et les synthesized properties dans ma classe :
les synthesized passent par les ivars :
- à l'initialisation, la classe prend les valeurs depuis le dico et les met dans les ivars.
- quand on appelle -dictionary ça fait l'inverse, ça prend de l'ivar et ça met dans le dico
Donc tant qu'on appelle pas le dictionary, bah y'a pas toute les opérations de tranformation qui pourrait prendre du temps dans une boucle.
Par contre les dynamic, y'a pas d'ivars, donc à chaque fois qu'on appelle le getter ou le setter ça transforme (dans un sens ou dans l'autre) et ça met dans le dictionnaire.
On utiliseras les dynamics pour les properties qu'on utilisera très peu : on gagne ainsi du temps à l'init de l'objet et à l'appel sur dictionary car du coup y'a rien à transformer puisque ça se fait au moment de l'appel du getter ou du setter.
Bon bah voilà , j'ai tout bougé dans le +initialize, merci pour la suggestion.
J'en ai profité pour vachement optimiser du coup. Et j'ai rajouté certaines exceptions pour éviter les appels explicites de +setupMappings, ou encore les appels de +map... en dehors du +setupMappings.