JXMagicObject : mapping facile entre dictionnaires et properties

JegnuXJegnuX Membre
août 2012 modifié dans Objective-C, Swift, C, C++ #1
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 image/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 image/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. image/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:@&quot;firstName&quot; toDictionaryKey:@&quot;name&quot;];<br />
	[self mapPropertyKey:@&quot;favoriteNumber&quot; toDictionaryKey:@&quot;favorite_number&quot;];<br />
	// you need a specific transformation<br />
	JXStringToDateValueTransformer *transformer = [[JXStringToDateValueTransformer alloc] init];<br />
  <br />
	[self mapPropertyKey:@&quot;birthdate&quot; toDictionaryKey:@&quot;birth_date&quot; 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 />
								  @&quot;Jerome&quot;, @&quot;name&quot;,<br />
								  @&quot;Alves&quot;, @&quot;lastName&quot;,<br />
								  @&quot;42&quot;, @&quot;favorite_number&quot;,<br />
								  @&quot;08/18/1989&quot;, @&quot;birth_date&quot;, nil];<br />
JXUser *user = [[JXUser alloc] initWithDictionary:userDict];<br />
<br />
NSLog(@&quot;%@ %@&quot;, user.firstName, user.lastName);<br />
user.favoriteNumber = 1337;<br />
user.birthDate = [NSDate date];<br />
	  <br />
NSLog(@&quot;Dictionary : %@&quot;, [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

Réponses

  • JegnuXJegnuX Membre
    juillet 2012 modifié #2
    roomful.jpeg









    tumblr_m335ezUmnI1qaydoj.jpg







    Non mais ouai si je pouvais avoir vos avis dessus, si vous trouvez ça utile, bien fait, si vous auriez fait certains truc différemment etc...

    Ou si vous voyez des horreurs dans le code aussi, je suis là  pour m'améliorer image/smile.png' class='bbc_emoticon' alt=':)' />
  • muqaddarmuqaddar Administrateur
    C'est très calme en ce moment... w-e et vacances... (je reviens ce jour de vacances par exemple).

    Patience.
  • Question bête, pourquoi faite ton setup dans une méthode d'instance ? Le setup sera le même pour toutes les instances de ta classe, il aurait été plus judicieux de le placer dans un +initialize ou un truc du genre histoire de définir une bonne fois pour toute tes mappings non ?
  • J'y avais juste pas vraiment pensé, mais il est vrai que c'est probablement plus pertinent. Je vais regarder ça.
  • FKDEVFKDEV Membre
    août 2012 modifié #6
    C'est vraiment très intéressant.



    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
  • 'FKDEV' a écrit:


    Quand on doit faire un mapping special, est-on obligé de redéfinir le mapping pour toutes les propriétés ?


    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.


    'FKDEV' a écrit:


    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 ?


    Je comprend pas la question image/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.
  • 'yoann' a écrit:


    Question bête, pourquoi faite ton setup dans une méthode d'instance ? Le setup sera le même pour toutes les instances de ta classe, il aurait été plus judicieux de le placer dans un +initialize ou un truc du genre histoire de définir une bonne fois pour toute tes mappings non ?




    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.
Connectez-vous ou Inscrivez-vous pour répondre.