Sözlük üyesini içeren Serialize Sınıfı


144

Daha önceki sorunumu genişletiyorum , ben de çalıştı benim yapılandırma dosya sınıf (seri) kararlaştırmaya karar verdim.

Şimdi (değer ağ yolu anahtar sürücü harfidir) haritaya ve kullanarak denedi sürücü harflerinin bir ilişkisel dizi depolamak istediğiniz Dictionary, HybridDictionaryve, Hashtablebunun için ama çağrılırken hep aşağıdaki hatayı alıyorum ConfigFile.Load()ya ConfigFile.Save():

'App.ConfigFile' türü yansıtılırken bir hata oluştu. [snip] System.NotSupportedException: Üye App.Configfile.mappedDrives [snip] serisini serileştiremiyor

Ne okudum Sözlükler ve HashTables serileştirilebilir, bu yüzden ne yanlış yapıyorum?

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

Yanıtlar:


77

IDictionary uygulayan bir sınıfı serileştiremezsiniz. Bu bağlantıya göz atın .

S: Neden hashtable'ları serileştiremiyorum?

Y: XmlSerializer, IDictionary arabirimini uygulayan sınıfları işleyemez. Bu kısmen program kısıtlamalarından ve kısmen de hashtable'ın XSD tipi sistemde bir karşılığı bulunmamasından kaynaklanıyordu. Tek çözüm, IDictionary arabirimini uygulamayan özel bir hashtable uygulamaktır.

Bu yüzden bunun için kendi Sözlük sürümünüzü oluşturmanız gerektiğini düşünüyorum. Bu diğer soruyu kontrol edin .


4
Sadece DataContractSerializersınıfın bunu yapabileceğini merak ediyorum . Sadece çıktı biraz çirkin.
2012'de rekor

187

Paul Welter'in Weblogunda bir çözüm var - XML ​​Serileştirilebilir Genel Sözlük

Nedense, .net 2.0 genel Sözlük XML serileştirilemez. Aşağıdaki kod snippet'i, bir xml serileştirilebilir genel sözlüktür. Sözlük, IXmlSerializable arabirimi uygulanarak serizable.

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
}

17
+1. Hey, Stack Overflow'un neden bir kopya kodu düğmesi yok? Hmmm? çünkü bu kod kopyalamaya değer!
toddmo

1
+1 Fantastik cevap. SortedList için de çalışır, sadece "SerializableDictionary" yerine "SerializableSortedList" ve "Dictionary <TKey, TValue>" "SortedList <TKey, TValue>" olarak değiştirildi.
kdmurray

1
+1 ve bir öneri. Bir SerializableDictionary nesnesi birden fazla öğe içeriyorsa, istisna atılır ... ReadXml () ve WriteXml () ReadStartElement ("item") taşımak için değiştirilmelidir; ve WriteStartElement ("öğe"); ve ilişkili ReadEndElement () ve WriteEndElement () ise while döngüsü dışında.
MNS

1
Bu, daha sonraki çerçevelerde IDictionary serileştirilebilir olduğu anlamına mı geliyor?
Thomas

1
Bu uygulama, sözde stringdeğerler InvalidOperationExceptiondepolıyorsa çalışacaktır, ancak özel nesneleri veya dize dizilerini depolamayı denerseniz beklenmedik bir sarmalayıcı öğesinden bahsederek serileştirmeyi kaldıracaktır . ( Bununla ilgili karşılaştığım sorunların bir örneği için soruma bakın .)
Christopher Kyle Horton

57

Kullanmak yerine XmlSerializera kullanabilirsiniz System.Runtime.Serialization.DataContractSerializer. Bu terimleri ve arayüzleri serileştirebilir.

İşte tam bir örnek, http://theburningmonk.com/2010/05/net-tips-xml-serialize-or-deserialize-dictionary-in-csharp/


2
En iyi cevap, eller aşağı.
DWRoelands

Anlaşıldı, bu en iyi cevap. Temiz, basit ve KURU (Kendinizi Tekrarlamayın).
Nolonar

DataContractSerializer ile ilgili sorun, alfabetik sırayla serileştirmesi ve serisini kaldırmasıdır, bu nedenle bir şeyi yanlış sırada serileştirmeye çalışırsanız, nesnenizdeki null özelliklerle sessizce başarısız olur
superjugy

14

Bir serileştirme vekili oluşturun.

Örneğin, Dictionary türünde public özelliği olan bir sınıfınız var.

Bu türün Xml serileştirmesini desteklemek için genel bir anahtar / değer sınıfı oluşturun:

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

Orijinal mülkünüze bir XmlIgnore özniteliği ekleyin:

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

SerializableKeyValue örnekleri dizisini barındıran ve SearchCategories özelliğini serileştirmek ve serisini kaldırmak için kullanılan dizi türünün genel bir özelliğini gösterin:

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

Sözlük üyesinden serileştirmeyi ayırdığı için bunu beğendim. Serileştirme yetenekleri eklemek istediğim yoğun bir şekilde kullanılan bir sınıfım olsaydı, sözlüğü sarmak miras alınan türlerde bir mola verebilir.
VoteCoffee

Bunu uygulayan herkes için dikkat edilmesi gereken bir nokta: vekil mülkünüzü bir Liste (veya başka bir koleksiyon ) yapmaya çalışırsanız , XML serileştiricisi ayarlayıcıyı çağırmaz (bunun yerine alıcıyı çağırır ve döndürülen listeye eklemeye çalışır, ki bu kesinlikle istediğin gibi değil). Bu desen için dizilere sadık kalın.
Fraxtil

9

Kullanımı oldukça kolay olan Json.Net'i keşfetmelisiniz ve Json nesnelerinin Sözlük'te doğrudan serileştirilmesine izin vermelisiniz.

james_newtonking

misal:

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

6

Sözlükler ve Hashtable'lar ile serileştirilemez XmlSerializer . Bu nedenle doğrudan kullanamazsınız. Bu sorunun çözümü,XmlIgnore bu özellikleri serileştiriciden gizlemek ve bunları serileştirilebilir anahtar / değer çiftlerinin bir listesi aracılığıyla göstermek için kullanmaktır.

Not: bir inşa etmek XmlSerializerçok pahalıdır, bu yüzden yeniden kullanma şansı varsa her zaman önbelleğe alın.


4

Paul Welter'ın sınıfına adapte olduğum için anahtar / değer için xml özniteliklerini kullanan bir SerializableDictionary sınıfı istedim.

Bu gibi xml üretir:

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

Kod:

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

Birim Testleri:

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

1
İyi görünüyor ama boş bir Sözlük ile başarısız. ReadXML yönteminde reader.IsEmptyElement testine ihtiyacınız vardır.
AnthonyVO

2

Dictionary sınıfı ISerializable uygular. Sınıf Sözlüğünün tanımı aşağıda verilmiştir.

[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  

Sorun olduğunu sanmıyorum. serileştirilemeyen başka bir veri türüne sahipseniz, sözlüğün serileştirilmeyeceğini söyleyen aşağıdaki bağlantıya bakın. http://forums.asp.net/t/1734187.aspx?Is+Dictionary+serializable+


Bu, en son sürümlerde doğrudur, ancak .NET 2'de Sözlük, bugün bile serileştirilemez. Ben sadece bugün bu iş parçacığı buldum .NET 3.5 hedefleyen bir proje ile doğruladı.
Bruce

2

ExtendedXmlSerializer'ı kullanabilirsiniz . Bir sınıfınız varsa:

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

ve bu sınıfın bir örneğini oluşturun:

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

Bu nesneyi ExtendedXmlSerializer kullanarak serileştirebilirsiniz:

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

Çıkış xml aşağıdaki gibi görünecektir:

<?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>

ExtendedXmlSerializer'ı nuget'ten yükleyebilir veya aşağıdaki komutu çalıştırabilirsiniz:

Install-Package ExtendedXmlSerializer

İşte çevrimiçi örnek


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.