Sérialisation xml d'un dictionary

Description

/*
  • Problématique :
  • Les tableaux, List, Collection et autres, contenus dans des objets
  • sont facilement sérialisables en XML dans la version 2.0 du Framework.
  • Mais les Framework 1.1 et 2.0 ne permettent cependant pas de sérialiser
  • des objets issus de la classe : Dictionary, qui est une classe plus complexe.
  • Objectifs :
  • Sérialiser un Dictionary sous forme XML
  • Désérialiser le document XML pour reconstituer le Dictionary
  • Remarque :
  • La sérialisation binaire n'est pas abordée ici.
  • Solution proposée :
  • Comme les Dictionary ne sont pas directement sérialisables en XML,
  • il faut enrichir les Dictionary proposés, par héritage, pour leurs ajouter
  • les méthodes nécessaires à leur sérialisation XML :
  • - Le Framework 1.1 (Visual Studio 2003) permettait de personnaliser
  • les Dictionary par héritage de spécialisation de la classe
  • abstraite DictionaryBase.
  • - Le Framework 2.0 (Visual Studio 2005) offre une autre approche.
  • On peut hériter de l'interface IDictionary pour personnaliser
  • son dictionnaire, et en héritant simultanément de l'interface
  • IXmlSerializable, qui apporte la solution à notre besoin de
  • sérialisation XML.
  • De nombreuses méthodes semblent alors devoir être implémentées...
  • Fonctionnement :
  • L'objet "mesClients", issu de la classe "Clients", contient deux "Client".
  • Elle est sérialisée dans le fichier : MonFichierXml1.xml
  • Ce fichier est désérialisé pour reconstituer un objet "tesClients"
  • qui est lui aussi sérialisé dans un autre fichier : MonFichierXml2.xml
  • Reste ensuite à comparer les deux fichiers pour s'assurer que l'on a rien
  • perdu au passage ...
  • Les fichiers de sérialisation sont normalement identiques.
  • Mais pas les objets "Client" qui ont perdu leur champs "private", normal.
  • Remarque :
  • De nombreuses classes du Framework 2.0 sont maintenant dites génériques.
  • Cette généricité permet de limiter les objets contenus dans une collection
  • par exemple.
  • Cet exemple utilise des classes génériques.
  • - Ancienne syntaxe
  • using System.Collections;
  • public Dictionary mesClients;
  • Et on pouvait alors mettre presque n'importe quoi dans ce dictionnaire !
  • - Nouvelle syntaxe
  • using System.Collections.Generic;
  • public Dictionary<String, Client> mesClients;
  • On ne peut mettre que des Client dans ce dictionnaire, avec une clef String.
  • Questions :
  • - Quelques points restent ici sans réponse (questions dans le code).
  • - Les événements d'erreur de la désérialisation semblent ne pas se déclencher !
  • - Dans WriteXml, la clef du dictionnaire génère la balise <string>dur</string>,
  • Comment obtenir <Clef>dur</Clef>, sans transformer le String en objet ?
  • - Est-il possible de forcer la sérialisation d'un champs "private" : m_DateNaissance ?
  • - L'utilisation de "using" comme instruction, afin de limiter la portée d'un variable
  • Essayez de le supprimer dans "Sérialisateur", et la désérialisation plante !
  • Si vous trouvez une solution à ces questions, je serais heureux d'en profiter.
  • Si vous pensez à d'autres techniques pour résoudre tout, ou partie, de ce problème
  • je serais content d'apprendre quelque chose de votre part. Merci à vous.
  • Une partie du code a été reprise sur le site MSDN de Microsoft. Merci à eux.
  • Si l'orthographe de ce document présente des lacunes, n'hésitez pas à m'en informer,
  • je serais attentif à vos remarques. (C'est sûr qu'il y a quelques erreurs !)
  • Environnement technique :
  • - Windows XP Pro,
  • - Visual Studio 2005,
  • - C# 2.0,
  • - XML.
  • /

Source / Exemple :


using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Xml;
using System.IO;

namespace WindowsApplication1
{
    public partial class Form1 : Form
    {
        /* 

  • Champs
  • /
String NomFichier1 = "MonFichierXml1.xml"; String NomFichier2 = "MonFichierXml2.xml"; Clients mesClients; Clients tesClients; // Sérialisateur XmlSerializer monSérialisateur = null; /*
  • Contructeurs
  • /
public Form1() { InitializeComponent(); } /*
  • Méthodes événementielles
  • /
private void Form1_Load(object sender, EventArgs e) { Client unClient; // Création de la collection des clients mesClients = new Clients(); // Ajout d'un client unClient = new Client("100", "Durand", "Pierre", @"01/02/2003"); mesClients.Add("dur", unClient); // Ajout d'un autre client unClient = new Client("200", "Martin", "Jean", @"04/05/2006"); mesClients.Add("mar", unClient); } private void Sérialiser_Click(object sender, EventArgs e) { Sérialiseur(mesClients, NomFichier1); MessageBox.Show("Consultez le fichier " + NomFichier1 + " afin de vérifier son contenu ..."); } private void Désérialiser_Click(object sender, EventArgs e) { tesClients = Désérialiseur(NomFichier1); Sérialiseur(tesClients, NomFichier2); // Pour vérification, par comparaison, avec NomFichier1. MessageBox.Show("Comparez les fichiers " + NomFichier1 + " et " + NomFichier2 + " afin de vérifier leur identité..."); } private void Quitter_Click(object sender, EventArgs e) { Application.Exit(); } /*
  • Méthodes
  • /
void Sérialiseur(Clients parClients, String parNomFichier) { try { /*
  • Création d'un sérialiseur de type : XmlSerializer
  • spécifiant le type de classe sérialisable : Clients.
  • /
monSérialisateur = new XmlSerializer(typeof(Clients)); // Paramétrage de l'écriture XML XmlWriterSettings ParamètresSérialisation = new XmlWriterSettings(); ParamètresSérialisation.Indent = true; ParamètresSérialisation.IndentChars = (" "); /*
  • Sérialisation
  • On peut utiliser aussi simplement : StreamWriter, ou FileWriter.
  • /
using (XmlWriter monEcrivain = XmlWriter.Create(parNomFichier, ParamètresSérialisation)) { monSérialisateur.Serialize(monEcrivain, mesClients); monEcrivain.Flush(); } } catch (Exception ex) { MessageBox.Show(ex.Message + "\n" + ex.InnerException.Message); } } Clients Désérialiseur(String parNomFichier) { /*
  • Création d'un sérialiseur de type : XmlSerializer
  • spécifiant le type de classe sérialisable : Clients.
  • /
monSérialisateur = new XmlSerializer(typeof(Clients)); // If the XML document has been altered with unknown // nodes or attributes, handles them with the // UnknownNode and UnknownAttribute events. monSérialisateur.UnknownNode +=new XmlNodeEventHandler(monSérialisateur_UnknownNode); monSérialisateur.UnknownAttribute += new XmlAttributeEventHandler(monSérialisateur_UnknownAttribute); // Paramétrage de l'écriture XML XmlReaderSettings ParamètresSérialisation = new XmlReaderSettings(); ParamètresSérialisation.ConformanceLevel = ConformanceLevel.Fragment; ParamètresSérialisation.IgnoreWhitespace = true; ParamètresSérialisation.IgnoreComments = true; // On peut utiliser aussi simplement : StreamReader, ou FileReader. XmlReader monLecteur = XmlReader.Create(parNomFichier, ParamètresSérialisation); /*
  • Uses the Deserialize method to restore the object's state
  • with data from the XML document.
  • /
return (Clients)monSérialisateur.Deserialize(monLecteur); } /*
  • Gestionnaires d'erreurs
  • /
private void monSérialisateur_UnknownNode(object sender, XmlNodeEventArgs e) { MessageBox.Show("Noeud inconnu : " + e.Name + "\t" + e.Text); } private void monSérialisateur_UnknownAttribute(object sender, XmlAttributeEventArgs e) { System.Xml.XmlAttribute attr = e.Attr; MessageBox.Show("Attribut inconnu : " + attr.Name + "='" + attr.Value + "'"); } } } public class Clients : IDictionary<String, Client>, IXmlSerializable /*
  • Toutes les interfaces de IDictionary sont implémentées par
  • simple "re-branchement" aux méthodes de : Dictionary.
  • La seule partie "créative" concerne les méthodes
  • de IXmlSerializable : GetSchema, WriteXml et ReadXml.
  • /
{ public IDictionary<String, Client> monDictionnaire; public Clients() { monDictionnaire = new Dictionary<String, Client>(); } #region IDictionary<string,Client> Membres public void Add(string key, Client value) { monDictionnaire.Add(key, value); } public bool ContainsKey(string key) { return monDictionnaire.ContainsKey(key); } public ICollection<string> Keys { get { return monDictionnaire.Keys; } } public bool Remove(string key) { return monDictionnaire.Remove(key); } public bool TryGetValue(string key, out Client value) { return monDictionnaire.TryGetValue(key,out value); } public ICollection<Client> Values { get { return monDictionnaire.Values; } } public Client this[string key] { get { return monDictionnaire[key]; } set { monDictionnaire[key] = value; } } #endregion #region ICollection<KeyValuePair<string,Client>> Membres public void Add(KeyValuePair<string, Client> item) { monDictionnaire.Add(item); } public void Clear() { monDictionnaire.Clear(); } public bool Contains(KeyValuePair<string, Client> item) { return monDictionnaire.Contains(item); } public void CopyTo(KeyValuePair<string, Client>[] array, int arrayIndex) { monDictionnaire.CopyTo(array, arrayIndex); } public int Count { get { return monDictionnaire.Count; } } public bool IsReadOnly { get { return monDictionnaire.IsReadOnly; } } public bool Remove(KeyValuePair<string, Client> item) { return monDictionnaire.Remove(item); } #endregion #region IEnumerable<KeyValuePair<string,Client>> Membres public IEnumerator<KeyValuePair<string, Client>> GetEnumerator() { return monDictionnaire.GetEnumerator(); } #endregion #region IEnumerable Membres System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return monDictionnaire.GetEnumerator(); } #endregion #region IXmlSerializable Membres public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(System.Xml.XmlReader reader) { reader.Read(); // move past container while (reader.NodeType != System.Xml.XmlNodeType.EndElement) { String key = (String)new XmlSerializer(typeof(String)).Deserialize(reader); reader.MoveToContent(); Client value = (Client)new XmlSerializer(typeof(Client)).Deserialize(reader); reader.MoveToContent(); monDictionnaire.Add(key,value); } } public void WriteXml(System.Xml.XmlWriter parEcrivain) { foreach(KeyValuePair<String, Client> unePaire in monDictionnaire) { new XmlSerializer(typeof(String)).Serialize(parEcrivain, unePaire.Key); // Comment remplacer ici <string> par <Clef> ? new XmlSerializer(typeof(Client)).Serialize(parEcrivain, unePaire.Value); } } #endregion } public class Client { /*
  • Ce champs sera sérialisé, car il est public
  • Mais le nom de la balise n'est pas joli pour XML !
  • /
public String m_CodeClient; /*
  • Ce champs sera sérialisé, car il est public
  • Mais le nom de la balise est modifié
  • pour être plus joli dans XML !
  • /
[XmlElement(ElementName="NomClient")] public String m_NomClient; /*
  • Celui-ci ne sera pas sérialisé, car il est privé
  • mais la propriété : PrénomClient, permettra la sérialisation !
  • /
private String m_PrénomClient; /*
  • Ce champs ne sera pas sérialisé du tout
  • car il est privé, sans propriété ...
  • /
private String m_DateNaissance; // Est-il possible de sérialiser ce champs, privé, par un attribut particulier ? public Client() { } public Client(String parCodeClient, String parNomClient, String parPrénomClient, String parDateNaissance) { m_CodeClient = parCodeClient; m_NomClient = parNomClient; PrénomClient = parPrénomClient; m_DateNaissance = parDateNaissance; } public String PrénomClient { get { return m_PrénomClient; } set { m_PrénomClient = value; } } }

Conclusion :

  • Questions :
  • - Quelques points restent ici sans réponse (questions dans le code).
  • - Les événements d'erreur de la désérialisation semblent ne pas se déclencher !
  • - Dans WriteXml, la clef du dictionnaire génère la balise <string>dur</string>,
  • Comment obtenir <Clef>dur</Clef>, sans transformer le String en objet ?
  • - Est-il possible de forcer la sérialisation d'un champs "private" : m_DateNaissance ?
  • - L'utilisation de "using" comme instruction, afin de limiter la portée d'un variable
  • Essayez de le supprimer dans "Sérialisateur", et la désérialisation plante !
  • Si vous trouvez une solution à ces questions, je serais heureux d'en profiter.
  • Si vous pensez à d'autres techniques pour résoudre tout, ou partie, de ce problème
  • je serais content d'apprendre quelque chose de votre part. Merci à vous.
  • Une partie du code a été reprise sur le site MSDN de Microsoft. Merci à eux.
  • Si l'orthographe de ce document présente des lacunes, n'hésitez pas à m'en informer,
  • je serais attentif à vos remarques. (C'est sûr qu'il y a quelques erreurs !)

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.