序列化包含字典成员的类
扩展我以前的问题 ,我已经决定(德)序列化我的configuration文件类效果很好。
我现在想要存储驱动器号映射(关键是驱动器号,值是networkingpath)的关联数组,并尝试使用Dictionary
, HybridDictionary
和Hashtable
为此但调用ConfigFile.Load()
时我总是得到以下错误ConfigFile.Load()
或ConfigFile.Save()
:
反映types“App.ConfigFile”的错误。 [snip] System.NotSupportedException:无法序列化成员App.Configfile.mappedDrives [snip]
从我读的字典和HashTables可以序列化,所以我做错了什么?
[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(); } } } }
你不能序列化一个实现了IDictionary的类。 看看这个链接 。
问:为什么我不能序列化哈希表?
答:XmlSerializer无法处理实现IDictionary接口的类。 这部分是由于日程安排的限制,部分原因是哈希表在XSDtypes系统中没有对应的事实。 唯一的解决scheme是实现一个不实现IDictionary接口的自定义哈希表。
所以我认为你需要为此创build自己的字典版本。 检查这个问题 。
在Paul Welter的Weblog – XML Serializable Generic Dictionary中有一个解决scheme
出于某种原因,.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 { #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 }
您可以使用System.Runtime.Serialization.DataContractSerializer
来代替使用XmlSerializer
。 这可以序列化字典和界面没有汗水。
这里是一个完整的例子, http:
创build一个序列化代理。
例如,您有一个types为Dictionary的公共属性的类。
要支持此types的Xml序列化,请创build一个通用的键值类:
public class SerializeableKeyValue<T1,T2> { public T1 Key { get; set; } public T2 Value { get; set; } }
将XmlIgnore属性添加到您的原始属性:
[XmlIgnore] public Dictionary<int, string> SearchCategories { get; set; }
公开一个数组types的公有属性,它包含一个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 ); } } }
你应该探索Json.Net,很容易使用,并允许Json对象直接在Dictionary中反序列化。
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
字典和散列表不能用XmlSerializer
进行序列化。 所以你不能直接使用它们。 解决方法是使用XmlIgnore
属性来从序列化程序中隐藏这些属性,并通过一系列可序列化的键值对来公开它们。
PS:构build一个XmlSerializer
是非常昂贵的,所以如果有机会重新使用它,总是要caching它。
我想要一个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; } } } }
unit testing:
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]); } } } }
这篇文章解释了如何处理这个问题: 我该如何…当应用程序需要它在C#中序列化哈希表?
我希望这是有帮助的
Dictionary类实现了ISerializable。 Class Dictionary的定义如下。
[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
我不认为这是问题。 参考下面的链接,其中说,如果你有任何其他数据types是不可序列化的,那么Dictionary将不会被序列化。 http://forums.asp.net/t/1734187.aspx?Is+Dictionary+serializable+
您可以使用ExtendedXmlSerializer 。 如果你有一个class级:
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>(); } }
并创build这个类的实例:
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
这里是在线的例子