डिक्शनरी के सदस्य वाले वर्ग को सीरियलाइज़ करें


144

अपनी पहले की समस्या पर विस्तार करते हुए , मैंने अपनी कॉन्फ़िग फ़ाइल क्लास को क्रमबद्ध (डी) करने का फैसला किया है , जिसने बहुत अच्छा काम किया है।

मैं अब मैप करने के लिए (कुंजी ड्राइव अक्षर है, मूल्य नेटवर्क पथ है) और उपयोग करने की कोशिश की है ड्राइव पत्र का एक साहचर्य सरणी संग्रहीत करना चाहते हैं Dictionary, HybridDictionaryऔर Hashtableइस के लिए लेकिन मैं हमेशा निम्न त्रुटि जब बुला पाने ConfigFile.Load()या ConfigFile.Save():

'App.ConfigFile' प्रकार को प्रतिबिंबित करने में एक त्रुटि थी। [स्निप] System.NotSupportedException: सदस्य को क्रमबद्ध नहीं कर सकते। App.Configfile.mappedDrives [स्निप]

मैंने जो शब्दकोश और हैशटैब पढ़े हैं, उन्हें क्रमबद्ध किया जा सकता है, इसलिए मैं क्या गलत कर रहा हूं?

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

जवाबों:


77

आप उस वर्ग को क्रमबद्ध नहीं कर सकते हैं जो IDEDIA को लागू करता है। इस लिंक को देखें

प्रश्न: मैं हैशटेबल्स को अनुक्रमित क्यों नहीं कर सकता?

A: XmlSerializer ID एन्क्रिप्शन इंटरफ़ेस को लागू करने वाली कक्षाओं को संसाधित नहीं कर सकता है। यह आंशिक रूप से शेड्यूल बाधाओं के कारण था और आंशिक रूप से इस तथ्य के कारण कि एक हैशटेबल में एक्सएसडी टाइप प्रणाली में एक समकक्ष नहीं है। एकमात्र समाधान एक कस्टम हैशटेबल को लागू करना है जो IDEDIA इंटरफ़ेस को लागू नहीं करता है।

इसलिए मुझे लगता है कि आपको इसके लिए शब्दकोश का अपना संस्करण बनाने की आवश्यकता है। इस अन्य प्रश्न की जाँच करें ।


4
बस DataContractSerializerवर्ग सोच रहा था कि ऐसा कर सकते हैं। बस आउटपुट थोड़ा बदसूरत है।
rekire

187

पॉल वेल्टर के वेबलॉग - एक्सएमएल सीरीयल जेनेरिक डिक्शनरी में एक समाधान है

किसी कारण से, .net 2.0 में जेनेरिक डिक्शनरी XML सीरियलसेबल नहीं है। निम्नलिखित कोड स्निपेट एक xml क्रमिक जेनेरिक शब्दकोश है। शब्दकोश IXmlSerializable इंटरफ़ेस को लागू करने से क्रमबद्ध है।

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। अरे, स्टैक ओवरफ्लो में कॉपी कोड बटन क्यों नहीं है? हममम? cuz यह कोड कॉपी करने लायक है!
टोडरमो

1
+1 शानदार जवाब। SortedList के लिए भी काम करता है, बस "Serializable Shadow" को "SerializableSreadList" और "Dictionary <TKey, TValue>" को "SortedList <TKey, TValue>" में बदल देता है।
कर्मद्रेय १२'१४ को

1
+1 और एक सुझाव। जब एक सीरियल-लोडर ऑब्जेक्ट एक से अधिक तत्वों को रखता है, तो अपवाद फेंक दिया जाता है ... ReadStartElement ("आइटम") को स्थानांतरित करने के लिए ReadXml () और WriteXml () को संशोधित किया जाना चाहिए; और WriteStartElement ("आइटम"); और इसके संबद्ध रीडएंडमेंट () और राइटएंडईमेंट () लूप से बाहर हैं।
MNS

1
क्या इसका मतलब यह है कि बाद के ढांचे में IDEDIA धारावाहिक है?
थॉमस

1
यह कार्यान्वयन तब काम करेगा जब शब्दकोश स्टोर कर रहा है, stringमान लें, लेकिन InvalidOperationExceptionयदि आप कस्टम ऑब्जेक्ट्स या स्ट्रिंग सरणियों को संग्रहीत करने का प्रयास करते हैं, तो एक अनपेक्षित आवरण तत्व का उल्लेख करते हुए डीरिएशन पर फेंक देंगे । ( जिन मुद्दों का मुझे सामना करना पड़ा, उनके एक उदाहरण के लिए मेरा प्रश्न देखें ।)
क्रिस्टोफर काइल हॉर्टन

57

उपयोग करने के बजाय XmlSerializerआप एक का उपयोग कर सकते हैंSystem.Runtime.Serialization.DataContractSerializer । यह शब्दकोशों को क्रमबद्ध कर सकता है और कोई पसीना नहीं इंटरफेस कर सकता है।

यहाँ एक पूर्ण उदाहरण के लिए एक लिंक है, http://theburningmonk.com/2010/05/net-tips-xml-serialize-or-deserialize-dEDIA-in-csharp/


2
सबसे अच्छा जवाब, हाथ नीचे।
DWRoelands

सहमत, यह सबसे अच्छा जवाब है। स्वच्छ, सरल और DRY (खुद को दोहराएं नहीं)।
नोलोनर

DataContractSerializer के साथ समस्या यह है कि यह क्रमबद्ध करता है और वर्णानुक्रम में deserializes है, इसलिए यदि आप गलत क्रम में किसी चीज़ का deserialize करने का प्रयास करते हैं, तो यह आपकी वस्तु में शून्य गुणों के साथ चुपचाप विफल हो जाएगा
Superjugit

14

एक क्रमबद्धता सरोगेट बनाएँ।

उदाहरण के लिए, आपके पास एक प्रकार की डिक्शनरी की सार्वजनिक संपत्ति है।

इस प्रकार के Xml क्रमांकन का समर्थन करने के लिए, एक सामान्य कुंजी-मूल्य वर्ग बनाएँ:

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

अपनी मूल संपत्ति में एक XmlIgnore विशेषता जोड़ें:

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

सरणी प्रकार की सार्वजनिक संपत्ति का पर्दाफाश करें, जो SerializableKeyValue उदाहरणों की एक सरणी रखता है, जिसका उपयोग सर्चकैरीजोरी संपत्ति में क्रमबद्ध और अस्वाभाविक बनाने के लिए किया जाता है:

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

मुझे यह पसंद है क्योंकि यह शब्दकोश सदस्य से क्रमबद्धता को कम करता है। यदि मेरे पास एक बहुत अधिक इस्तेमाल किया जाने वाला वर्ग था जिसे मैं क्रमबद्धता क्षमताओं को जोड़ना चाहता था, तो शब्दकोश को लपेटने से विरासत में मिले प्रकारों के साथ एक विराम हो सकता है।
वोट कॉफ़ी

इसे लागू करने वाले किसी व्यक्ति के लिए सावधानी का एक शब्द: यदि आप अपनी किराए की संपत्ति को एक सूची (या किसी अन्य संग्रह ) बनाने की कोशिश करते हैं , तो एक्सएमएल धारावाहिककर्ता सेटर को कॉल नहीं करेगा (इसके बजाय यह गेट्टर को कॉल करता है और वापस लौटी सूची में जोड़ने की कोशिश करता है, जो स्पष्ट रूप से वह नहीं है जो आप चाहते थे)। इस पैटर्न के लिए सरणियों के लिए छड़ी।
फ्रैक्स्टिल

9

आपको Json.Net का पता लगाना चाहिए, इसका उपयोग करना काफी आसान है और सीधे Json ऑब्जेक्ट्स को डिक्शनरी में डिस्क्रिब्लाइज़ करने की अनुमति देता है।

james_newtonking

उदाहरण:

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

शब्दकोश और हैशटैब के साथ क्रमिक नहीं हैं XmlSerializer। इसलिए आप उन्हें सीधे उपयोग नहीं कर सकते। XmlIgnoreधारावाहिक से उन संपत्तियों को छिपाने के लिए विशेषता का उपयोग करने के लिए एक वर्कअराउंड होगा और उन्हें अनुक्रमिक कुंजी-मूल्य वाले जोड़े की सूची के माध्यम से उजागर किया जाएगा।

पुनश्च: निर्माण एक XmlSerializerबहुत महंगा है, इसलिए यदि इसे फिर से उपयोग करने में सक्षम होने का मौका है, तो इसे हमेशा कैश करें।


4

मैं एक Serializable मंदोदरी वर्ग चाहता था जो कुंजी / मान के लिए xml विशेषताएँ का उपयोग करता था इसलिए मैंने पॉल वेल्टर की कक्षा को अनुकूलित किया है।

यह xml का उत्पादन करता है जैसे:

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

कोड:

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

यूनिट टेस्ट:

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
अच्छा लगता है, लेकिन यह एक खाली शब्दकोश के साथ विफल रहता है। ReadXML विधि में आपको Reader.IsEmptyElement परीक्षण की आवश्यकता है।
एंथोनीवो

2

शब्दकोश वर्ग ISerializable लागू करता है। नीचे दिए गए कक्षा शब्दकोश की परिभाषा।

[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  

मुझे नहीं लगता कि यह समस्या है। नीचे दिए गए लिंक का संदर्भ लें, जो कहता है कि यदि आपके पास कोई अन्य डेटा प्रकार है, जो धारावाहिक नहीं है, तो शब्दकोश को क्रमबद्ध नहीं किया जाएगा। http://forums.asp.net/t/1734187.aspx?Is+Dictionary+serializable+


नवीनतम संस्करणों में यह सच है, लेकिन .NET 2 में, शब्दकोश आज भी क्रमबद्ध नहीं है। मैंने आज ही इसकी पुष्टि .NET 3.5 को लक्षित करने वाले एक प्रोजेक्ट के साथ की, जो कि मुझे यह धागा मिला।
ब्रूस

2

आप ExtendedXmlSerializer का उपयोग कर सकते हैं । यदि आपके पास एक वर्ग है:

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

और इस वर्ग का उदाहरण बनाएं:

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

आप ExtendedXmlSerializer का उपयोग करके इस ऑब्जेक्ट को क्रमबद्ध कर सकते हैं:

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

आउटपुट xml जैसा दिखेगा:

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

आप nuget से ExtendedXmlSerializer स्थापित कर सकते हैं या निम्न कमांड चला सकते हैं:

Install-Package ExtendedXmlSerializer

यहाँ ऑनलाइन उदाहरण है


0

यह आलेख ठीक से बताता है कि इसे कैसे संभालना है: मैं कैसे करूँ ... सी # में एक हैश तालिका को सीरिज़ करें जब एप्लिकेशन को इसकी आवश्यकता होती है?

मुझे आशा है कि यह मददगार है


लिंक-केवल उत्तर बिट सड़ांध के अधीन हैं, और आवश्यक विवरण के साथ उत्तर निकाय में ही संक्षेप में प्रस्तुत किया जाना चाहिए।
क्रिस्टोफर काइल हॉर्टन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.