代码之家  ›  专栏  ›  技术社区  ›  dragonmantank

序列化包含字典成员的类

  •  125
  • dragonmantank  · 技术社区  · 17 年前

    扩展我的 earlier problem ,我决定(反)序列化我的配置文件类,这非常有效。

    我现在想存储要映射的驱动器号的关联数组(键是驱动器号,值是网络路径),并尝试使用 Dictionary HybridDictionary Hashtable 但是我在打电话时总是遇到以下错误 ConfigFile.Load() ConfigFile.Save() :

    反射类型出错 “App.ConfigFile”。[剪报] System.NotSupportedException:无法 序列化成员 App.Configfile.mappedDrive[snip]

    根据我所读的,字典和哈希表可以序列化,那么我做错了什么?

    [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();
                }
            }
        }
    }
    
    10 回复  |  直到 9 年前
        1
  •  190
  •   Jbjstam    8 年前

    目前有一个解决办法 Paul Welter's Weblog - XML Serializable Generic Dictionary

    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
    }
    
        2
  •  78
  •   Community Mohan Dere    9 年前

    link .

    实现IDictionary的类 界面这部分是由于 进度限制,部分原因是: 在XSD类型中有一个对应项 系统唯一的解决办法是 实现一个自定义哈希表,该哈希表 不实现IDictionary 界面

    因此,我认为您需要为此创建自己版本的词典。检查这个 other question .

        3
  •  59
  •   Richard Ev    12 年前

    而不是使用 XmlSerializer System.Runtime.Serialization.DataContractSerializer . 这可以毫不费力地序列化字典和接口。

    下面是一个完整示例的链接, http://theburningmonk.com/2010/05/net-tips-xml-serialize-or-deserialize-dictionary-in-csharp/

        4
  •  14
  •   user2921681    12 年前

    创建序列化代理。

    例如,您有一个公共属性为Dictionary类型的类。

    要支持此类型的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实例数组,这些实例用于序列化和反序列化为SearchCategories属性:

        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 );
                }
            }
        }
    
        5
  •  9
  •   Chandra Sekhar    14 年前

    您应该浏览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
  •  7
  •   David Schmitt    17 年前

    XmlSerializer XmlIgnore 属性对序列化程序隐藏这些属性,并通过可序列化的键值对列表公开它们。

    PS:构建一个 XmlSerializer 它非常昂贵,因此如果有可能重复使用,请始终将其缓存。

        7
  •  5
  •   Benbob    12 年前

    我想要一个SerializableDictionary类,它使用xml属性作为键/值,所以我修改了Paul Welter的类。

    这将生成类似以下内容的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]);
                }
            }
        }
    }
    
        8
  •  2
  •   Saikrishna    11 年前

    [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+

        9
  •  2
  •   Wojciech Nagórski    9 年前

    你可以用 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 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>
    

    您可以从安装ExtendedXmlSerializer nuget 或运行以下命令:

    Install-Package ExtendedXmlSerializer
    

    这是 online example

        10
  •  0
  •   Community Mohan Dere    11 年前
        11
  •  0
  •   ankit    4 年前

    您可以使用System.Runtime.Serialization的DataContractSerialization。这将能够序列化IDictionary和Dictionary成员。

    https://docs.microsoft.com/en-us/dotnet/framework/wcf/samples/datacontractserializer-sample

    找到下面的代码片段。

    public  ConfigFile ExtractConfigFileFromXml(string xmlPath)
    {
        var serializer = new DataContractSerializer(typeof(ConfigFile));
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.DtdProcessing = DtdProcessing.Parse;
        XmlReader reader = XmlReader.Create(xmlPath, settings);
        var confile = (ConfigFile)serializer.ReadObject(reader);
        return confile;
     }