Serializzare la class contenente il membro del dizionario

Espandendo il mio problema precedente , ho deciso di (serializzare) la mia class di file di configurazione che funzionava alla grande.

Ora voglio memorizzare un array associativo di lettere di unità da mappare (la chiave è la lettera di unità, il valore è il percorso di rete) e ho provato ad usare Dictionary , HybridDictionary e Hashtable per questo ma ottengo sempre il seguente errore quando si chiama ConfigFile.Load() o ConfigFile.Save() :

Si è verificato un errore nel tipo “App.ConfigFile”. [snip] System.NotSupportedException: Imansible serializzare membro App.Configfile.mappedDrives [snip]

Da quello che ho letto, Dictionaries e HashTables possono essere serializzati, quindi cosa sto facendo di sbagliato?

 [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(); } } } } 

Non è ansible serializzare una class che implementa IDictionary. Dai un’occhiata a questo link .

D: Perché non posso serializzare gli hashtables?

A: XmlSerializer non può elaborare classi che implementano l’interfaccia IDictionary. Ciò è in parte dovuto ai limiti del programma e in parte al fatto che una tabella hash non ha una controparte nel sistema di tipo XSD. L’unica soluzione è implementare una tabella hash personalizzata che non implementa l’interfaccia IDictionary.

Quindi penso che tu abbia bisogno di creare la tua versione del dizionario per questo. Controlla questa altra domanda .

C’è una soluzione al Weblog di Paul Welter – XML ​​Generic Serializable Dictionary

Per qualche ragione, il dizionario generico in .net 2.0 non è serializzabile in XML. Il seguente snippet di codice è un dizionario generico serializzabile xml. Il dizionario è serializzabile implementando l’interfaccia 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 } 

Invece di usare XmlSerializer puoi usare System.Runtime.Serialization.DataContractSerializer . Questo può serializzare dizionari e interfacce senza problemi.

Ecco un link per un esempio completo, http://theburningmonk.com/2010/05/net-tips-xml-serialize-or-deserialize-dictionary-in-csharp/

Creare un surrogato di serializzazione.

Ad esempio, hai una class con proprietà pubbliche di tipo Dizionario.

Per supportare la serializzazione Xml di questo tipo, creare una class valore-chiave generica:

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

Aggiungi un attributo XmlIgnore alla tua proprietà originale:

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

Esporre una proprietà pubblica di tipo array, che contiene una matrice di istanze SerializableKeyValue, utilizzate per serializzare e deserializzare nella proprietà 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 ); } } } 

Dovresti esplorare Json.Net, abbastanza facile da usare e permettere agli oggetti Json di essere deserializzati nel dizionario direttamente.

james_newtonking

esempio:

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

Dizionari e Hashtables non sono serializzabili con XmlSerializer . Pertanto non è ansible utilizzarli direttamente. Una soluzione alternativa sarebbe utilizzare l’attributo XmlIgnore per hide tali proprietà dal serializzatore ed esporle tramite un elenco di coppie chiave-valore serializzabili.

PS: la costruzione di un XmlSerializer è molto costosa, quindi conservala sempre in cache se esiste la possibilità di riutilizzarla.

Volevo una class SerializableDictionary che usasse gli attributi xml per chiave / valore, quindi ho adattato la class di Paul Welter.

Questo produce xml come:

     " 

Codice:

 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; } } } } 

Test unitari:

 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 class Dictionary implementa ISerializable. La definizione di Dizionario di class fornita di seguito.

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

Non penso che sia questo il problema. fare riferimento al link sottostante, che dice che se si dispone di un altro tipo di dati che non è serializzabile, il dizionario non verrà serializzato. http://forums.asp.net/t/1734187.aspx?Is+Dictionary+serializable+

È ansible utilizzare ExtendedXmlSerializer . Se hai una class:

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

e crea un’istanza di questa class:

 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"); 

Puoi serializzare questo object usando ExtendedXmlSerializer:

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

L’output xml sarà simile a:

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

È ansible installare ExtendedXmlSerializer da nuget o eseguire il seguente comando:

 Install-Package ExtendedXmlSerializer 

Ecco un esempio online

Questo articolo spiega esattamente come gestirlo: Come posso … Serializzare una tabella hash in C # quando l’applicazione lo richiede?

Spero che questo sia utile