Programmation C sharp/La sérialisation

La sérialisation est un procédé d'entrée-sortie permettant de sauvegarder et recharger l'état d'un objet. Cette fonctionnalité permet de faire abstraction du format de fichier utilisé. Celui-ci dépend de l'outil de sérialisation utilisé.

Programmation C#
Programmation C#
Modifier ce modèle

L'état d'un objet correspond à l'ensemble des valeurs de ses champs. Les propriétés sont calculées en fonction des champs, et le code des méthodes ne change pas au cours de l'exécution.

Attributs

modifier

L'attribut System.SerializableAttribute marque les classes dont les instances peuvent être sérialisées. Si l'attribut est absent pour une classe, la sérialisation de ses instances provoquera une exception.

Exemple :

[Serializable]
class Facture
{
    public string Client;
    public double TotalHT;
    public double TotalTTC;
}

L'attribut System.NonSerializedAttribute marque les champs qu'il ne faut pas enregistrer. C'est le cas des champs dont on peut retrouver la valeur par calcul, par exemple.

Exemple :

[Serializable]
class Facture
{
    public string Client;
    public double TotalHT;

    [NonSerialized]
    public double TotalTTC; // = TotalHT * (1 + taux_TVA/100)
}

Quand la classe évolue, de nouveaux champs sont ajoutés à la classe, d'autres sont retirés. L'attribut System.Runtime.Serialization.OptionalFieldAttribute marque les champs optionnels lors de la désérialisation (lecture de l'objet). Il est donc possible de marquer les nouveaux champs comme optionnels, et garder les anciens champs (marqués optionnels également) pour garder la compatibilité avec les anciens fichiers.

Exemple :

[Serializable]
class Facture
{
    public string Client;
    public double TotalHT;

    [NonSerialized]
    public double TotalTTC;

    [OptionalField]
    public string AdresseLivraison; // Nouveau champ
}

Dans cet exemple, la classe Facture permettra de lire des fichiers d'objets Facture contenant le champ AdresseLivraison ou non.

La sérialisation

modifier

Le format de sérialisation dépend de la classe utilisée pour sérialiser les objets.

La classe System.Runtime.Serialization.Formatter

modifier

Cette classe abstraite définit les méthodes suivantes :

void Serialize(
    System.IO.Stream serializationStream,
    object graph);
Cette méthode enregistre l'objet graph dans le flux d'entrée-sortie spécifié.
object Deserialize(
    System.IO.Stream serializationStream);
Cette méthode retourne l'objet lu depuis le flux d'entrée-sortie spécifié.

Les classes dérivées définissent un format concret de sérialisation :

  • La classe System.Runtime.Serialization.Formatters.Binary.BinaryFormatter permet de sérialiser dans un format binaire,
  • La classe System.Runtime.Serialization.Formatters.Soap.SoapFormatter permet de sérialiser au format SOAP.

Une classe dérivée de System.Runtime.Serialization.Formatter sérialise les attributs et les évènements d'un objet, quel que soit l'accès associé (public, protégé, privé, ou par défaut).

Format XML

modifier

La classe System.Xml.Serialization.XmlSerializer permet de sérialiser au format XML. Elle ne dérive pas de la classe System.Runtime.Serialization.Formatter, et possède les méthodes suivantes :

void Serialize(
    System.IO.Stream stream,
    object o);
void Serialize(
    System.IO.TextWriter textWriter,
    object o);
void Serialize(
    System.Xml.XmlWriter xmlWriter,
    object o);
Ces méthodes enregistrent l'objet o dans le flux d'entrée-sortie spécifié.
object Deserialize(
    System.IO.Stream stream);
object Deserialize(
    System.IO.TextReader textReader);
object Deserialize(
    System.Xml.XmlReader xmlReader);
Ces méthodes retournent l'objet lu depuis le flux d'entrée-sortie spécifié.

Pour le format XML, les attributs Serializable et NonSerialized sont ignorés :

  • Toute classe est sérialisable en XML,
  • L'attribut System.Xml.Serialization.XmlIgnoreAttribute marque les champs à ignorer lors de la sérialisation.

La classe System.Xml.Serialization.XmlSerializer sérialise les attributs et les propriétés en lecture/écriture publics d'un objet, si la valeur n'est pas nulle. Donc la sérialisation en XML ignore :

  • les attributs et propriétés retournant une valeur nulle,
  • les attributs et propriétés protégés ou privés,
  • les propriétés en lecture seule (impossible de les désérialiser),
  • les propriétés en écriture seule (impossible de les sérialiser).

Pour en savoir plus :

Sérialisation personnalisée

modifier

Il est possible de personnaliser la manière de sérialiser un objet en implémentant l'interface System.Runtime.Serialization.ISerializable.

Cette interface n'a qu'une méthode, invoquée lors de la sérialisation :

void GetObjectData (
    SerializationInfo info,
    StreamingContext context )

Mais la classe doit également comporter le constructeur suivant, invoqué lors de la désérialisation :

protected NomDeClasse(SerializationInfo info, StreamingContext context)

L'objet de type SerializationInfo permet la sérialisation et la désérialisation de l'objet. Chaque valeur sauvegardée est associé à un nom unique. Cet objet possède les méthodes suivantes :

public void AddValue(string name, T value) // tout type de valeur
public object GetValue(string name, Type type)
public T GetType(string name) // GetByte, GetChar, GetInt16, GetDecimal, GetDateTime, ...

L'implémentation de la méthode GetObjectData fait appel à la méthode AddValue de l'objet info pour ajouter une valeur à sauvegarder. Le constructeur utilise la méthode GetValue ou les méthodes GetType pour retrouver la valeur sauvegardée.

Exemple :

[Serializable]
public class Personne : ISerializable
{
    private string nom;
    private int age;

    public Personne() { }

    protected Personne(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
            throw new System.ArgumentNullException("info");

        nom = (string)info.GetValue("Nom", typeof(string));
        age = (int)info.GetValue("Age", typeof(int));
    }

    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
            throw new System.ArgumentNullException("info");

        info.AddValue("Nom", nom);
        info.AddValue("Age", age);
    }
}