it-swarm.com.de

Klasse mit Dictionary-Mitglied serialisieren

Als Erweiterung meines früheres Problem habe ich beschlossen, meine Konfigurationsdateiklasse zu (de) serialisieren, was großartig funktioniert hat.

Ich möchte jetzt ein assoziatives Array von Laufwerksbuchstaben für die Zuordnung speichern (Schlüssel ist der Laufwerksbuchstabe, Wert ist der Netzwerkpfad) und habe versucht, Dictionary, HybridDictionary und Hashtable zu verwenden. ] dazu bekomme ich aber beim Aufruf von ConfigFile.Load() oder ConfigFile.Save() immer folgende Fehlermeldung:

Beim Wiedergeben des Typs "App.ConfigFile" ist ein Fehler aufgetreten. [snip] System.NotSupportedException: Mitglied App.Configfile.mappedDrives kann nicht serialisiert werden [snip]

Nach dem, was ich gelesen habe, können Wörterbücher und HashTables serialisiert werden. Was mache ich also falsch?

[XmlRoot(ElementName="Config")]
public class ConfigFile
{
    public String guiPath { get; set; }
    public string configPath { get; set; }
    public Dictionary<string, string> mappedDrives = new Dictionary<string, string>();

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

Sie können keine Klasse serialisieren, die IDictionary implementiert. Check out this link .

F: Warum kann ich keine Hashtabellen serialisieren?

A: Der XmlSerializer kann keine Klassen verarbeiten, die die IDictionary-Schnittstelle implementieren. Dies war teilweise auf Zeitplanbeschränkungen und teilweise auf die Tatsache zurückzuführen, dass eine Hash-Tabelle kein Gegenstück im XSD-Typensystem hat. Die einzige Lösung besteht darin, eine benutzerdefinierte Hashtabelle zu implementieren, die die IDictionary-Schnittstelle nicht implementiert.

Ich denke, Sie müssen hierfür eine eigene Version des Wörterbuchs erstellen. Überprüfen Sie diese andere Frage .

70
bruno conde

Es gibt eine Lösung unter Paul Welters Weblog - XML ​​Serializable Generic Dictionary

Aus irgendeinem Grund kann das generische Wörterbuch in .net 2.0 nicht mit XML serialisiert werden. Das folgende Code-Snippet ist ein serialisierbares generisches XML-Wörterbuch. Das Wörterbuch kann durch Implementierung der IXmlSerializable-Schnittstelle serialisiert werden.

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue>
    : Dictionary<TKey, TValue>, IXmlSerializable
{
    public SerializableDictionary() { }
    public SerializableDictionary(IDictionary<TKey, TValue> dictionary) : base(dictionary) { }
    public SerializableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) : base(dictionary, comparer) { }
    public SerializableDictionary(IEqualityComparer<TKey> comparer) : base(comparer) { }
    public SerializableDictionary(int capacity) : base(capacity) { }
    public SerializableDictionary(int capacity, IEqualityComparer<TKey> 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
}
176
osman pirci

Anstelle von XmlSerializer können Sie auch ein System.Runtime.Serialization.DataContractSerializer. Dadurch können Wörterbücher serialisiert und Schnittstellen nicht ins Schwitzen gebracht werden.

Hier ist ein Link zu einem vollständigen Beispiel: http://theburningmonk.com/2010/05/net-tips-xml-serialize-or-deserialize-dictionary-in-csharp/

54
Despertar

Erstellen Sie einen Serialisierungsersatz.

Beispiel: Sie haben eine Klasse mit einer öffentlichen Eigenschaft vom Typ Dictionary.

Erstellen Sie eine generische Schlüsselwertklasse, um die XML-Serialisierung dieses Typs zu unterstützen:

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

Fügen Sie Ihrer ursprünglichen Eigenschaft ein XmlIgnore-Attribut hinzu:

    [XmlIgnore]
    public Dictionary<int, string> SearchCategories { get; set; }

Stellen Sie eine öffentliche Eigenschaft des Array-Typs bereit, die ein Array von SerializableKeyValue-Instanzen enthält, die zum Serialisieren und Deserialisieren in der SearchCategories-Eigenschaft verwendet werden:

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

Sie sollten sich mit Json.Net befassen, das recht einfach zu verwenden ist und das direkte Deserialisieren von Json-Objekten im Dictionary ermöglicht.

james_newtonking

beispiel:

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

Wörterbücher und Hashtables können nicht mit XmlSerializer serialisiert werden. Daher können Sie sie nicht direkt verwenden. Eine Problemumgehung wäre, das Attribut XmlIgnore zu verwenden, um diese Eigenschaften vor dem Serializer auszublenden und sie über eine Liste serialisierbarer Schlüssel-Wert-Paare verfügbar zu machen.

PS: Das Konstruieren eines XmlSerializer ist sehr teuer, also speichern Sie es immer im Cache, wenn die Möglichkeit besteht, dass Sie es wiederverwenden können.

6
David Schmitt

Ich wollte eine SerializableDictionary-Klasse, die XML-Attribute für Schlüssel/Wert verwendet, also habe ich die Klasse von Paul Welter angepasst.

Dies erzeugt xml wie:

<Dictionary>
  <Item Key="Grass" Value="Green" />
  <Item Key="Snow" Value="White" />
  <Item Key="Sky" Value="Blue" />
</Dictionary>"

Code:

using System.Collections.Generic;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace DataTypes {
    [XmlRoot("Dictionary")]
    public class SerializableDictionary<TKey, TValue>
        : Dictionary<TKey, TValue>, 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<TKey, TValue>));
            foreach (XElement item in doc.Descendants(XName.Get("Item"))) {
                using(XmlReader itemReader =  item.CreateReader()) {
                    var kvp = serializer.Deserialize(itemReader) as SerializableKeyValuePair<TKey, TValue>;
                    this.Add(kvp.Key, kvp.Value);
                }
            }
            reader.ReadEndElement();
        }

        public void WriteXml(System.Xml.XmlWriter writer) {
            XmlSerializer serializer = new XmlSerializer(typeof(SerializableKeyValuePair<TKey, TValue>));
            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("", "");
            foreach (TKey key in this.Keys) {
                TValue value = this[key];
                var kvp = new SerializableKeyValuePair<TKey, TValue>(key, value);
                serializer.Serialize(writer, kvp, ns);
            }
        }
        #endregion

        [XmlRoot("Item")]
        public class SerializableKeyValuePair<TKey, TValue> {
            [XmlAttribute("Key")]
            public TKey Key;

            [XmlAttribute("Value")]
            public TValue Value;

            /// <summary>
            /// Default constructor
            /// </summary>
            public SerializableKeyValuePair() { }
        public SerializableKeyValuePair (TKey key, TValue value) {
            Key = key;
            Value = value;
        }
    }
}
}

Unit-Tests:

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<string, string>();
            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<string, string>;
                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<int, int>();
            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<int, int>;
                Assert.AreEqual(dict[4], outDict[4]);
                Assert.AreEqual(dict[5], outDict[5]);
                Assert.AreEqual(dict[7], outDict[7]);
            }
        }
    }
}
4
Keyo

Sie können ExtendedXmlSerializer verwenden. Wenn Sie eine Klasse haben:

public class ConfigFile
{
    public String guiPath { get; set; }
    public string configPath { get; set; }
    public Dictionary<string, string> mappedDrives {get;set;} 

    public ConfigFile()
    {
        mappedDrives = new Dictionary<string, string>();
    }
}

und erstelle eine Instanz dieser Klasse:

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

Sie können dieses Objekt mit ExtendedXmlSerializer serialisieren:

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

Ausgabe-XML sieht folgendermaßen aus:

<?xml version="1.0" encoding="utf-8"?>
<ConfigFile type="Program+ConfigFile">
    <guiPath>guiPath</guiPath>
    <configPath>configPath</configPath>
    <mappedDrives>
        <Item>
            <Key>Mouse</Key>
            <Value>Logitech MX Master</Value>
        </Item>
        <Item>
            <Key>keyboard</Key>
            <Value>Microsoft Natural Ergonomic Keyboard 4000</Value>
        </Item>
    </mappedDrives>
</ConfigFile>

Sie können ExtendedXmlSerializer von nuget aus installieren oder den folgenden Befehl ausführen:

Install-Package ExtendedXmlSerializer

Hier ist Online-Beispiel

2
Wojtpl2

die Dictionary-Klasse implementiert ISerializable. Die unten angegebene Definition des Klassenwörterbuchs.

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

Ich denke nicht, dass das das Problem ist. Verweisen Sie auf den folgenden Link, der besagt, dass Dictionary nicht serialisiert wird, wenn Sie einen anderen nicht serialisierbaren Datentyp verwenden. http://forums.asp.net/t/1734187.aspx?Is+Dictionary+serializable+

2
Saikrishna

Dieser Artikel beschreibt genau, wie Sie damit umgehen: Wie kann ich ... eine Hash-Tabelle in C # serialisieren, wenn die Anwendung dies erfordert?

Ich hoffe das ist hilfreich

0
Nissim