none
Changement de format de XML RRS feed

  • Question

  •  

    Bonjour,

     

    J'ai écris un outil en C# qui stocke des informations dans un fichier XML, et grâce aux attributs  [Serializable], [XmlElement("XXX")] de mes classes c’est magique, les chargements et les constructions d'objets se font tout seul .

    Mon problème est que mon format de XML évolue au cours du temps, et que je souhaite faire des conversions d’anciens vers nouveaux formats.

    Pour l’instant la solution que j’ai trouvé, c’est de « namespacer » les classes que j’utilise à la serialisation, par exemple :

     

    namespace Xml.V1_1

    {

     

        #region MaClasse

        /// <summary>

        /// Description of MaClasse.

        /// </summary>

        ///

        [Serializable]

        public class MaClasse

        {

            #region Serializable members

            [XmlElement("Name")]

            public String Name;

            #endregion

     

    ...

            #endregion

     

        }

        #endregion

     

    Au sein de mes XML je conserve une balise de processing qui ressemble à ça :

    <?fmt Format="1.1"?>

     

    Au sein de mon projet C#, je conserve la progression de toutes les évolutions de mes classes, chacune dans un namespace Xml.VX_XX.

    A chaque chargement je vérifie la cohérence entre la signature de ma classe de chargement courante, et cette balise. S’il y a incohérence :

    -          je deserialise dans l’ancien format

    -          construit un objet au nouveau format, à partir de l’objet précédent

    -          je serialise dans le nouveau format

     

    Ca fonctionne très bien, sauf que la fonction de conversion d’un format à un autre est un peut lourde à écrire, et a tendance à se complexifier aux grés des évolutions.

    Pour l’instant ça ressemble un peu à ça :

            public static void ConvertV1_0ToV1_1(String path)

            {

                V1_0. MaClasse projV1_0 = V1_0. MaClasseDeserializer.Deserialize(path);

                V1_1. MaClasse projV1_1 = new V1_1. MaClasse ();

     

                projV1_1.Name = projV1_0.Name;

    ...

     

                V1_1.MaClasseSerializer.Serialize(path, projV1_1);

            }

     

    Ma question est, existe-t-il des mécanismes en C# plus subtils, plus élégants pour faire ça ? Avoir une gestion des compatibilités nécessitant le moins de maintenance possible ?

     

    J’ai eu l’occasion de regarder les XSD, sans rentrer dans le détail, mais ce n’est pas une solution que j’ai retenu, car à partir d’un XML relativement simple, ça ma produit des classes C# assez complexes…

     

    Merci
    jeudi 3 avril 2008 08:52

Réponses

  • Bonjour,

     

    Il y a tout un chapitre sur ça dans le livre de préparation du MCTS mais je vais essayer de résumer à l'essentiel et surtout aux mécanismes plus subtils que tu imagines

     

    Grosso modo il y a 2 solutions:

    1- mettre en oeuvre une sérialisation personnalisée (Implémenter ISerializable) mais que je n'aborderai pas ici sauf si vraiment tu ne t'en sors pas avec l'autre méthode (a ce moment là je te ferai un exemple)

    2- utiliser l'attribut OptionalField que je vais expliciter ci dessous

     

    Tout se base sur ta classe d'origine.

    Imaginons par exemple

    [Serializable]class Personnage: IDeserializationCallback

    {

    public String Nom;

    public String Prenom;

    [OptionalField]

    public Int32 Age;

    void IDeserializationCallback.OnDeserialization(Object sender)

    {

    //Faire les init des nouveaux champs

    }

    }

     

    Nous avons ici une classe qui à l'origine n'avait que Nom et Prenom et qui vient d'avoir un nouveau champ de type Age

    L'attribut [OptionalField] permet de le préciser.

    Cela devrait te simplifier fortement les évolutions de classe.

    Les contraintes de cette méthode (qui obligent à passer sur la méthode 1) sont:

    - tu ne peut pas supprimer un champ sérialisé

    - tu ne peux pas modifier un champ sérialisé (type ou nom)

     

    Cette méthode te permet de te passer d'un convertisseur  et si les contraintes te conviennent ça devrait pas mal te simplifier la vie.

     

    Bonne sérialization (j'aime beaucoup sérialiser depuis que je l'ai découvert)

     

    jeudi 3 avril 2008 14:58
  • Merci Radric,

     

    Effectivement ça fonctionne très bien avec [OptionalField], c'est idéal pour les rajouts/changements de balises, mais comme tu le précises, pour gérer les champs à retirer il faut faire autrement.

     

    Je pense m'en sortir avec un parcours des balises tout simple, au cours duquel je nettoierais mon fichiers XML des vielles balises.

     

    Par contre, chose étonnante j'ai essayé ton interface IDeserializationCallback, mais ma callback n'est jamais appelée ??

    Je dois mal m'y prendre.

     

    Encore merci Wink

    lundi 7 avril 2008 14:21

Toutes les réponses

  • Bonjour,

     

    Il y a tout un chapitre sur ça dans le livre de préparation du MCTS mais je vais essayer de résumer à l'essentiel et surtout aux mécanismes plus subtils que tu imagines

     

    Grosso modo il y a 2 solutions:

    1- mettre en oeuvre une sérialisation personnalisée (Implémenter ISerializable) mais que je n'aborderai pas ici sauf si vraiment tu ne t'en sors pas avec l'autre méthode (a ce moment là je te ferai un exemple)

    2- utiliser l'attribut OptionalField que je vais expliciter ci dessous

     

    Tout se base sur ta classe d'origine.

    Imaginons par exemple

    [Serializable]class Personnage: IDeserializationCallback

    {

    public String Nom;

    public String Prenom;

    [OptionalField]

    public Int32 Age;

    void IDeserializationCallback.OnDeserialization(Object sender)

    {

    //Faire les init des nouveaux champs

    }

    }

     

    Nous avons ici une classe qui à l'origine n'avait que Nom et Prenom et qui vient d'avoir un nouveau champ de type Age

    L'attribut [OptionalField] permet de le préciser.

    Cela devrait te simplifier fortement les évolutions de classe.

    Les contraintes de cette méthode (qui obligent à passer sur la méthode 1) sont:

    - tu ne peut pas supprimer un champ sérialisé

    - tu ne peux pas modifier un champ sérialisé (type ou nom)

     

    Cette méthode te permet de te passer d'un convertisseur  et si les contraintes te conviennent ça devrait pas mal te simplifier la vie.

     

    Bonne sérialization (j'aime beaucoup sérialiser depuis que je l'ai découvert)

     

    jeudi 3 avril 2008 14:58
  • Merci Radric,

     

    Effectivement ça fonctionne très bien avec [OptionalField], c'est idéal pour les rajouts/changements de balises, mais comme tu le précises, pour gérer les champs à retirer il faut faire autrement.

     

    Je pense m'en sortir avec un parcours des balises tout simple, au cours duquel je nettoierais mon fichiers XML des vielles balises.

     

    Par contre, chose étonnante j'ai essayé ton interface IDeserializationCallback, mais ma callback n'est jamais appelée ??

    Je dois mal m'y prendre.

     

    Encore merci Wink

    lundi 7 avril 2008 14:21