Serialize Class que contiene el miembro Dictionary

Ampliando mi problema anterior , he decidido (de) serializar mi clase de archivo de configuración, que funcionó muy bien.

Ahora quiero almacenar una matriz asociativa de letras de unidad en el mapa (la clave es la letra de la unidad, el valor es la ruta de red) y he intentado usar Dictionary , HybridDictionary y Hashtable para esto, pero siempre ConfigFile.Load() el siguiente error al llamar a ConfigFile.Load() o ConfigFile.Save() :

Hubo un error que refleja el tipo ‘App.ConfigFile’. [snip] System.NotSupportedException: No se puede serializar el miembro App.Configfile.mappedDrives [snip]

Por lo que he leído, Dictionaries y HashTables se pueden serializar, entonces, ¿qué estoy haciendo mal?

 [XmlRoot(ElementName="Config")] public class ConfigFile { public String guiPath { get; set; } public string configPath { get; set; } public Dictionary mappedDrives = new Dictionary(); public Boolean Save(String filename) { using(var filestream = File.Open(filename, FileMode.OpenOrCreate,FileAccess.ReadWrite)) { try { var serializer = new XmlSerializer(typeof(ConfigFile)); serializer.Serialize(filestream, this); return true; } catch(Exception e) { MessageBox.Show(e.Message); return false; } } } public void addDrive(string drvLetter, string path) { this.mappedDrives.Add(drvLetter, path); } public static ConfigFile Load(string filename) { using (var filestream = File.Open(filename, FileMode.Open, FileAccess.Read)) { try { var serializer = new XmlSerializer(typeof(ConfigFile)); return (ConfigFile)serializer.Deserialize(filestream); } catch (Exception ex) { MessageBox.Show(ex.Message + ex.ToString()); return new ConfigFile(); } } } } 

No puede serializar una clase que implemente IDictionary. Mira este enlace .

P: ¿Por qué no puedo serializar hashtables?

A: XmlSerializer no puede procesar clases implementando la interfaz de IDictionary. Esto se debió en parte a restricciones de progtwigción y en parte debido al hecho de que una tabla hash no tiene una contraparte en el sistema de tipo XSD. La única solución es implementar una tabla hash personalizada que no implemente la interfaz IDictionary.

Entonces creo que necesitas crear tu propia versión del diccionario para esto. Mira esta otra pregunta .

Hay una solución en Weblog de Paul Welter – XML ​​Serializable Generic Dictionary

Por algún motivo, el diccionario genérico en .net 2.0 no es XML serializable. El siguiente fragmento de código es un diccionario genérico serializable xml. El diccionario se puede serializar implementando la interfaz IXmlSerializable.

 using System; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; [XmlRoot("dictionary")] public class SerializableDictionary : Dictionary, IXmlSerializable { public SerializableDictionary() { } public SerializableDictionary(IDictionary dictionary) : base(dictionary) { } public SerializableDictionary(IDictionary dictionary, IEqualityComparer comparer) : base(dictionary, comparer) { } public SerializableDictionary(IEqualityComparer comparer) : base(comparer) { } public SerializableDictionary(int capacity) : base(capacity) { } public SerializableDictionary(int capacity, IEqualityComparer comparer) : base(capacity, comparer) { } #region IXmlSerializable Members public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(System.Xml.XmlReader reader) { XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); bool wasEmpty = reader.IsEmptyElement; reader.Read(); if (wasEmpty) return; while (reader.NodeType != System.Xml.XmlNodeType.EndElement) { reader.ReadStartElement("item"); reader.ReadStartElement("key"); TKey key = (TKey)keySerializer.Deserialize(reader); reader.ReadEndElement(); reader.ReadStartElement("value"); TValue value = (TValue)valueSerializer.Deserialize(reader); reader.ReadEndElement(); this.Add(key, value); reader.ReadEndElement(); reader.MoveToContent(); } reader.ReadEndElement(); } public void WriteXml(System.Xml.XmlWriter writer) { XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); foreach (TKey key in this.Keys) { writer.WriteStartElement("item"); writer.WriteStartElement("key"); keySerializer.Serialize(writer, key); writer.WriteEndElement(); writer.WriteStartElement("value"); TValue value = this[key]; valueSerializer.Serialize(writer, value); writer.WriteEndElement(); writer.WriteEndElement(); } } #endregion } 

En lugar de usar XmlSerializer , puede usar System.Runtime.Serialization.DataContractSerializer . Esto puede serializar diccionarios e interfaces sin problemas.

Aquí hay un enlace a un ejemplo completo, http://theburningmonk.com/2010/05/net-tips-xml-serialize-or-deserialize-dictionary-in-csharp/

Crea un sustituto de serialización.

Ejemplo, tienes una clase con propiedad pública de tipo Diccionario.

Para admitir la serialización Xml de este tipo, cree una clase genérica de valor-clave:

 public class SerializeableKeyValue { public T1 Key { get; set; } public T2 Value { get; set; } } 

Agregue un atributo XmlIgnore a su propiedad original:

  [XmlIgnore] public Dictionary SearchCategories { get; set; } 

Exponer una propiedad pública del tipo de matriz, que contiene una matriz de instancias de SerializableKeyValue, que se utilizan para serializar y deserializar en la propiedad SearchCategories:

  public SerializeableKeyValue[] SearchCategoriesSerializable { get { var list = new List>(); if (SearchCategories != null) { list.AddRange(SearchCategories.Keys.Select(key => new SerializeableKeyValue() {Key = key, Value = SearchCategories[key]})); } return list.ToArray(); } set { SearchCategories = new Dictionary(); foreach (var item in value) { SearchCategories.Add( item.Key, item.Value ); } } } 

Debería explorar Json.Net, bastante fácil de usar y permite que los objetos Json se deserialicen en Diccionario directamente.

james_newtonking

ejemplo:

 string json = @"{""key1"":""value1"",""key2"":""value2""}"; Dictionary values = JsonConvert.DeserializeObject>(json); Console.WriteLine(values.Count); // 2 Console.WriteLine(values["key1"]); // value1 

Diccionarios y Hashtables no son serializables con XmlSerializer . Por lo tanto, no puede usarlos directamente. Una solución alternativa sería utilizar el atributo XmlIgnore para ocultar esas propiedades del serializador y exponerlas a través de una lista de pares clave-valor serializables.

PD: la construcción de un XmlSerializer es muy costosa, por lo que siempre debe almacenarla en caché si existe la posibilidad de poder volver a utilizarla.

Quería una clase SerializableDictionary que utilizara atributos xml para clave / valor, así que he adaptado la clase de Paul Welter.

Esto produce xml como:

     " 

Código:

 using System.Collections.Generic; using System.Xml; using System.Xml.Linq; using System.Xml.Serialization; namespace DataTypes { [XmlRoot("Dictionary")] public class SerializableDictionary : Dictionary, IXmlSerializable { #region IXmlSerializable Members public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { XDocument doc = null; using (XmlReader subtreeReader = reader.ReadSubtree()) { doc = XDocument.Load(subtreeReader); } XmlSerializer serializer = new XmlSerializer(typeof(SerializableKeyValuePair)); foreach (XElement item in doc.Descendants(XName.Get("Item"))) { using(XmlReader itemReader = item.CreateReader()) { var kvp = serializer.Deserialize(itemReader) as SerializableKeyValuePair; this.Add(kvp.Key, kvp.Value); } } reader.ReadEndElement(); } public void WriteXml(System.Xml.XmlWriter writer) { XmlSerializer serializer = new XmlSerializer(typeof(SerializableKeyValuePair)); XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); foreach (TKey key in this.Keys) { TValue value = this[key]; var kvp = new SerializableKeyValuePair(key, value); serializer.Serialize(writer, kvp, ns); } } #endregion [XmlRoot("Item")] public class SerializableKeyValuePair { [XmlAttribute("Key")] public TKey Key; [XmlAttribute("Value")] public TValue Value; ///  /// Default constructor ///  public SerializableKeyValuePair() { } public SerializableKeyValuePair (TKey key, TValue value) { Key = key; Value = value; } } } } 

Pruebas unitarias:

 using System.IO; using System.Linq; using System.Xml; using System.Xml.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace DataTypes { [TestClass] public class SerializableDictionaryTests { [TestMethod] public void TestStringStringDict() { var dict = new SerializableDictionary(); dict.Add("Grass", "Green"); dict.Add("Snow", "White"); dict.Add("Sky", "Blue"); dict.Add("Tomato", "Red"); dict.Add("Coal", "Black"); dict.Add("Mud", "Brown"); var serializer = new System.Xml.Serialization.XmlSerializer(dict.GetType()); using (var stream = new MemoryStream()) { // Load memory stream with this objects xml representation XmlWriter xmlWriter = null; try { xmlWriter = XmlWriter.Create(stream); serializer.Serialize(xmlWriter, dict); } finally { xmlWriter.Close(); } // Rewind stream.Seek(0, SeekOrigin.Begin); XDocument doc = XDocument.Load(stream); Assert.AreEqual("Dictionary", doc.Root.Name); Assert.AreEqual(dict.Count, doc.Root.Descendants().Count()); // Rewind stream.Seek(0, SeekOrigin.Begin); var outDict = serializer.Deserialize(stream) as SerializableDictionary; Assert.AreEqual(dict["Grass"], outDict["Grass"]); Assert.AreEqual(dict["Snow"], outDict["Snow"]); Assert.AreEqual(dict["Sky"], outDict["Sky"]); } } [TestMethod] public void TestIntIntDict() { var dict = new SerializableDictionary(); dict.Add(4, 7); dict.Add(5, 9); dict.Add(7, 8); var serializer = new System.Xml.Serialization.XmlSerializer(dict.GetType()); using (var stream = new MemoryStream()) { // Load memory stream with this objects xml representation XmlWriter xmlWriter = null; try { xmlWriter = XmlWriter.Create(stream); serializer.Serialize(xmlWriter, dict); } finally { xmlWriter.Close(); } // Rewind stream.Seek(0, SeekOrigin.Begin); XDocument doc = XDocument.Load(stream); Assert.AreEqual("Dictionary", doc.Root.Name); Assert.AreEqual(3, doc.Root.Descendants().Count()); // Rewind stream.Seek(0, SeekOrigin.Begin); var outDict = serializer.Deserialize(stream) as SerializableDictionary; Assert.AreEqual(dict[4], outDict[4]); Assert.AreEqual(dict[5], outDict[5]); Assert.AreEqual(dict[7], outDict[7]); } } } } 

la clase Dictionary implementa ISerializable. La definición de Diccionario de clases dada a continuación.

 [DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))] [DebuggerDisplay("Count = {Count}")] [Serializable] [System.Runtime.InteropServices.ComVisible(false)] public class Dictionary: IDictionary, IDictionary, IReadOnlyDictionary, ISerializable, IDeserializationCallback 

No creo que ese sea el problema. consulte el siguiente enlace, que dice que si tiene cualquier otro tipo de datos que no es serializable, el diccionario no se serializará. http://forums.asp.net/t/1734187.aspx?Is+Dictionary+serializable+

Puede usar ExtendedXmlSerializer . Si tienes una clase:

 public class ConfigFile { public String guiPath { get; set; } public string configPath { get; set; } public Dictionary mappedDrives {get;set;} public ConfigFile() { mappedDrives = new Dictionary(); } } 

y crea una instancia de esta clase:

 ConfigFile config = new ConfigFile(); config.guiPath = "guiPath"; config.configPath = "configPath"; config.mappedDrives.Add("Mouse", "Logitech MX Master"); config.mappedDrives.Add("keyboard", "Microsoft Natural Ergonomic Keyboard 4000"); 

Puede serializar este objeto usando ExtendedXmlSerializer:

 ExtendedXmlSerializer serializer = new ExtendedXmlSerializer(); var xml = serializer.Serialize(config); 

El resultado xml se verá así:

   guiPath configPath   Mouse Logitech MX Master   keyboard Microsoft Natural Ergonomic Keyboard 4000    

Puede instalar ExtendedXmlSerializer desde nuget o ejecutar el siguiente comando:

 Install-Package ExtendedXmlSerializer 

Aquí está el ejemplo en línea

Este artículo explica exactamente cómo manejar esto: ¿Cómo puedo … Serializar una tabla hash en C # cuando la aplicación lo requiera?

Espero que esto sea útil