Magazine

C# : Sérialisation Xml et Hashtable

Publié le 11 octobre 2007 par Olivier Duval

La sérialisation est un merveilleux mécanisme qui permet de conserver l’état d’un objet (persistance) à l’instant T. La sérialisation peut revêtir plusieurs formats : XML, JSON, YAML, binaire, ...selon la technologie utilisée (.NET, Java, Ruby, Prototype, ...).

.NET supporte en natif la sérialisation (XML notamment), ce qui permet de prendre une photo de ses objets et de les conserver en base (ou dans un fichier selon), pour peu que vos classes soient marquées par l’attribut [Serializable].

Imaginons maintenant que vous utilisiez dans une classe une Hashtable pour stocker une suite de noms / valeurs, la sérialisation XML de cette classe devient impossible en raison de l’implémentation de IDictionary de la classe Hashtable.

Classe qu’on aimerait avoir :

[Serializable] public class ServiceParams { #region IServiceParams Members private string _action; public string Action { get { return _action; } set { _action = value; } } private Hashtable _dico = new Hashtable(); public Hashtable ParamsService { get { return _dico; } set { _dico = value; } } #endregion }

La sérialisation d’un objet de ce type provoquera une erreur : “The type System.Collections.Hashtable is not supported because it implements IDictionary”.

Afin d’éviter cela, une technique, si l’on souhaite utiliser une Hashtable avec la sérialisation en XML (XmlSerializer) : introduire une classe qu’on nommera SerializableHashtable qui implémente l’interface IXmlSerializable et qui utilise un objet intermédiaire (mapp la Hash sur celui-ci) sérialisable (List<Node>).

L’interface IXmlSerializable permet d’avoir des méthodes ad-hoc pour spécifier comment sérialiser (WriteXml) ou désérialiser (ReadXml) l’objet.

Ce qui donne :

using System.Collections; using System.Collections.Generic; using System.Xml; using System.Xml.Serialization; [Serializable] public class ServiceParams : IServiceParams { #region IServiceParams Members private string _action; public string Action { get { return _action; } set { _action = value; } } private SerializableHashtable _dico = new SerializableHashtable(); public SerializableHashtable ParamsService { get { return _dico; } set { _dico = value; } } #endregion } // optionnel, une interface toute personnelle public interface IServiceParams { /// <summary> /// Le discriminant si besoin /// </summary> string Action { get;set;} /// <summary> /// Paramètres /// </summary> SerializableHashtable ParamsService { get; set;} } /// <summary> /// Classe qui permet de sérialiser une hashtable grâce à une List<Node> /// </summary> [Serializable] public class SerializableHashtable : Hashtable, IXmlSerializable { private XmlSerializer xs = new XmlSerializer(typeof(List<Node>)); public SerializableHashtable() { } /// <summary> /// La classe qui mappera la Hashtable /// </summary> [Serializable] public class Node { public Node() { } public Node(string k, object v) { key = k; val = v; } public string key; public object val; } #region IXmlSerializable Members /// <summary> /// Sérialisation /// on parcourt le Hashtable pour stocker clé+valeur dans List<Node> /// on sérialise List<Node> /// </summary> /// <param name="writer"></param> void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { List<Node> list = new List<Node>(); foreach(string key in this.Keys) list.Add(new Node(key, this[key])); xs.Serialize(writer,list); } public System.Xml.Schema.XmlSchema GetSchema() { // TODO: Add SerializableHashtable.GetSchema implementation return null; } /// <summary> /// Désérialisation /// on déséralise List<Node> /// on parcours List<Node> et on construit le Hashtable /// </summary> /// <param name="reader"></param> void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { reader.Read(); List<Node> list = xs.Deserialize(reader) as List<Node>; if(list == null) return; for (int i = 0; i < list.Count; i++) { Node node = list[i] as Node; this.Add(node.key, node.val); } } #endregion }

On est maintenant prêt à gérer ses Hashtable sérialisables.

using System.IO; using System.Xml; using System.Xml.Serialization; //... public void maSuperMethod() { XmlSerializer _serializer = new XmlSerializer(typeof(ServiceParams)); ServiceParams myParams = new ServiceParams(); myParams.Action = "DoIt"; myParams.ParamsService.Add("id","10"); myParams.ParamsService.Add("libelle","mon objet"); // sérialisation StringWriter strw = new StringWriter(); XmlTextWriter sw = new XmlTextWriter(strw); sw.Formatting = Formatting.Indented; _serializer.Serialize(sw, myParams); sw.Close(); string xml = strw.ToString(); // désérialisation ServiceParams _params = (ServiceParams)_serializer.Deserialize(new StringReader(xml)); }

L’objet sérializé myParams, stocké dans xml donnera le résultat suivant :

<?xml version="1.0" encoding="utf-16"?> <ServiceParams xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Action>DoIt</Action> <ParamsService> <ArrayOfNode> <Node> <key>libelle</key> <val xsi:type="xsd:string">mon objet</val> </Node> <Node> <key>id</key> <val xsi:type="xsd:string">10</val> </Node> </ArrayOfNode> </ParamsService> </ServiceParams>

Dans l’absolu, on pourrait sérialiser la Hashtable en utilisant le BinaryFormatter. Dans ce cas, le résultat ne donnerait pas du XML, et donc, il deviendrait difficile (à la main) de modifier les valeurs…cela peut bien évidemment arriver. XmlSerializer reste un bon compromis pour ce type d’utilisation.


Retour à La Une de Logo Paperblog

A propos de l’auteur


Olivier Duval 4 partages Voir son profil
Voir son blog

l'auteur n'a pas encore renseigné son compte l'auteur n'a pas encore renseigné son compte