Sérialiser des données non-plist dans une plist
AliGator
Membre, Modérateur
Bonjour les gens,
Voilà je cherche à sérialiser une classe qui a des variables d'instance scalaires, en particulier un NSPoint, pour sauver mes données dans un plist au final.
En fait j'ai un tableau de ces objets NamedPoints, chaque NamedPoint contenant une NSString et un NSPoint. Et je voudrais qu'ai final ces NSPoints soient convertis, par exemple en un NSDictionary avec les clés "x" et "y", au moment de la sérialisation, pour pouvoir obtenir un plist (puisqu'un NSPoint ne peut être encodé dans un plist de base)
1) J'ai déjà réussi à implémenter NSCoding pour sauver mon tableau de points sur disque (avec encodePoint:forKey: etc)... Mais le hic c'est que ça me produit un format propriétaire (une archive, quoi). Certes quand on regarde c'est un plist, mais avec des clés CF$UID et des trucs bizarres... pas ce que je veux quoi puisque moi je veux un plist tel qu'on le trouve quand on archive un NSArray classique (quand il ne contient que des plist-objets)
2) Dans la doc de "Archives and Serializations Programming Guide" j'ai aussi vu NSPropertyListSerialization, mais qui semble ne permettre de manipuler que les classes autorisées dans les PList. Ils expliquent bien comment substituer une NSColor par sa représentation NSData (dans l'exemple de la doc de NSUserDefaults vers laquelle ils renvoient) mais c'est à appeler sur chaque NSColor à archiver (dans mon cas ça serait sur chaque NSPoint... et encore NSPoint est une structure, pas une classe)
Il y a aussi quelques méthodes indiquant comment "prendre la main au moment de la sérialisation pour remplacer un objet par une représentation" (et c'est pile ce que je veux)... mais c'est indiqué que c'est deprecated et obsolète (classes NSSerializer et NSDeserializer) car remplacé par NSPropertyListSerialization....
Du coup je vois pas trop comment je pourrais dire à ma classe NamedPoint "ah bah si tu veux t'encoder sous forme de plist, encode ce NSPoint sous forme de ce NSDictionary puisque tu ne sais pas stocker des NSPoint"...
Je pourrais créer un NSArray (parallèle à mon tableau de NamedPoints) contenant la représentation que je veux pour ma sauvegarde (un NSArray de NSDictionary, chaque dico ayant une clé name, x et y), et sauver ce tableau... Mais l'idéal serait plutôt "d'expliquer" à ma classe NamedPoint comment s'encoder pour lorsqu'elle doit être sérialisée dans un plist, sur le même principe que encodeWithCoder et initWithCoder mais pour les plist, quoi...
Des idées pour faire ça propre, du coup ?
Voilà je cherche à sérialiser une classe qui a des variables d'instance scalaires, en particulier un NSPoint, pour sauver mes données dans un plist au final.
En fait j'ai un tableau de ces objets NamedPoints, chaque NamedPoint contenant une NSString et un NSPoint. Et je voudrais qu'ai final ces NSPoints soient convertis, par exemple en un NSDictionary avec les clés "x" et "y", au moment de la sérialisation, pour pouvoir obtenir un plist (puisqu'un NSPoint ne peut être encodé dans un plist de base)
1) J'ai déjà réussi à implémenter NSCoding pour sauver mon tableau de points sur disque (avec encodePoint:forKey: etc)... Mais le hic c'est que ça me produit un format propriétaire (une archive, quoi). Certes quand on regarde c'est un plist, mais avec des clés CF$UID et des trucs bizarres... pas ce que je veux quoi puisque moi je veux un plist tel qu'on le trouve quand on archive un NSArray classique (quand il ne contient que des plist-objets)
2) Dans la doc de "Archives and Serializations Programming Guide" j'ai aussi vu NSPropertyListSerialization, mais qui semble ne permettre de manipuler que les classes autorisées dans les PList. Ils expliquent bien comment substituer une NSColor par sa représentation NSData (dans l'exemple de la doc de NSUserDefaults vers laquelle ils renvoient) mais c'est à appeler sur chaque NSColor à archiver (dans mon cas ça serait sur chaque NSPoint... et encore NSPoint est une structure, pas une classe)
Il y a aussi quelques méthodes indiquant comment "prendre la main au moment de la sérialisation pour remplacer un objet par une représentation" (et c'est pile ce que je veux)... mais c'est indiqué que c'est deprecated et obsolète (classes NSSerializer et NSDeserializer) car remplacé par NSPropertyListSerialization....
Du coup je vois pas trop comment je pourrais dire à ma classe NamedPoint "ah bah si tu veux t'encoder sous forme de plist, encode ce NSPoint sous forme de ce NSDictionary puisque tu ne sais pas stocker des NSPoint"...
Je pourrais créer un NSArray (parallèle à mon tableau de NamedPoints) contenant la représentation que je veux pour ma sauvegarde (un NSArray de NSDictionary, chaque dico ayant une clé name, x et y), et sauver ce tableau... Mais l'idéal serait plutôt "d'expliquer" à ma classe NamedPoint comment s'encoder pour lorsqu'elle doit être sérialisée dans un plist, sur le même principe que encodeWithCoder et initWithCoder mais pour les plist, quoi...
Des idées pour faire ça propre, du coup ?
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Faire une catégorie pour le désarchivage initWithContentsOfFile: usingConventions:
Mais bon, ça ne résout pas grand chose, dans tous les cas, il faut qu'une classe transforme le point.
Soit je reconstruit comme je disais un NSArray contenant un NSDictionary par NamedPoint à sauver, et je crée donc ça à la main avec une boucle for qui transforme chaque NamedPoint en NSDictionary... le temps de sauver ce NSArray temporaire... Mais ça je voudrais éviter car ce n'est pas évolutif (si c'est un NSDictionary de NSArray de ... de NamedPoints que je dois sauver un jour ? Je reproduit toute l'arborescence à chaque fois ? berk)
Soit je trouve un moyen de dire à ma classe NamedPoint "bon si on te demande de te sérialiser/sauver sur disque/..., n'essaye pas de te sauver toi-même (car tu réaliserais que tu contiens un NSPoint qui n'est pas un "Plist-Object" donc tu refuserais), mais essaye de sauver ta "représentation plist-ique".
En gros avoir une sorte de méthode -(id)substituteObjectForSerialisation et -(void)initWithSubstitueObject: ... ou une sorte de ValueTransformer ou je ne sais quoi, qui permettrait à mon NamedPoint de fournir une représentation de lui-même juste pour la sauvegarde dans un plist.
Un peu comme encodeWithCoder permet à un objet d'indiquer comment il doit être encodé pour la sérialisation... Ou comme classForCoder, replacementObjectForCoder etc... de NSCoding (mais moi je veux un plist, pas une archive)
Cela c'est facile à faire, une NSString (description ou autres), un NSData fait l'affaire (encode). Le problème est dans le désarchivage, car on n'a pas le moyen de mettre un tag dans le plist indiquant la règle du désarchivage.
A moins ... de mettre un métada dans le NSData (un entête dans la NSString) et de faire une custom-méthode (sans doute récursive) de lecture du plist. C'est l'idée de initWithContentsOfFile: usingConventions:
Ce que je ferais à ta place, si ta classe NamedPoints ne contiens que une NSString, et un NSPoint c'est remplacer ça par un dictionnaire avec 3 clef name, x, y
Ensuite tu peut te créer des macro ou fonction du style npIntForX(dict) qui te renvois le résultat de [[dict valueForKey:@x] intValue]
Autre possibilité à tester, sous classer NSDictionary pour avoir une API compatible avec ta classe actuelle et faire des getter et des setters qui gère la correspondance.
NSDictionary est un classe "Cluster". On ne peut pas sous classer.
C'est pour ça que j'ai mis a tester, même si j'aurais du mettre a vérifier, je n'étais plus certain du fait que NSDictionary soit cluster ou pas
Philippe, je pense que tu n'as pas compris exactement ce que je demandais. Faire une méthode, moi-même, qui convertit mon NamedPoint en une classe plist-isable (un NSDictionary par exemple), ça bien sûr que ce n'est pas méchant. Avec la méthode description ou autre d'ailleurs qui peut aussi faire l'affaire.
Le truc c'est que si j'appelle "writeToFile:atomically:" sur mon NSArray contenant mes NamedPoints, il va refuser (me retourner NO) puisque les objets contenus dans le NSArray ne sont pas plist-ables, en particulier les NSPoints de mes NamedPoints...
La difficulté c'est pas de créer une méthode qui me retourne un NSDictionary pour représenter mon NamedPoint pour la sauvegarde... c'est de trouver s'il n'y a pas une méthode existante pour ça automatiquement appelée sur tes objets quand on fait writeToFile:atomically: sur un NSArray.
Pour que quand je fasse [namedPointsArray writeToFile:toto atomically:YES] il m'écrive tout seul le plist en demandant à chacun de mes NamedPoint une représentation plist-ique d'eux-même (via cette fameuse hypothétique méthode prévue pour) pour pouvoir les écrire dans le plist justement.
Et ça c'est pas gagné apparemment...
Si, c'est bien ce que j'avais compris, mais pour moi cela passe par des méthodes de catégorie sur les collections NSArray et NSDictionary à implémenter parce que le fonctionnement des méthodes clés writeToFile et initWithContentsOfFile est opaque. Il faudrait une méthode qui renvoie une array/dictionary où les objets sont transformés si ils appartiennent à un certain groupe de classes, la récursion étant un peu pénible je l'accorde.
Intéressant, je vais finir par le laisser piéger à ton truc ! ::)
Une autre piste, à voir jusqu'où on peut la pousser : surclasser NSArray/NSDictionary , et définir un protocole formel ou informel.
et dans le code des tests pour voir si les méthodes -(id)serializedObject et -(void)initWithSerializedObject: sont implémentées par les objets de l'array.
Ca y est, j'ai craqué !
Un petit essai :
et le plist créé
Merci, je tâche de voir ça. Du coup faut faire une catégorie pour tous les types acceptés dans une plist (NSArray et NSDictionary surtout en tout cas), je vais creuser aussi
Je veux parler de l'utilisation de structures C dans un langage objet.
Franchement les NSRect et consorts pouah !
J'ai utilisé les bidouilles décrites plus haut, pour gérer les prefs des applis et les prefs des documents (packages).
J'ai deux classes AppPrefs et SheetPrefs qui se gèrent comme un NSUserDefauts/NSDictionary, mais comme ce ne sont ni un NSUserDefauts ni un dictionnaire, j'utilise du forwardInvocation ...
Ou comment faire un truc crade 100% cocoa compliant