C# 序列化包含字典成员的类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/495647/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Serialize Class containing Dictionary member
提问by dragonmantank
Expanding upon my earlier problem, I've decided to (de)serialize my config file class which worked great.
扩展我之前的问题,我决定(反)序列化我的配置文件类,效果很好。
I now want to store an associative array of drive letters to map (key is the drive letter, value is the network path) and have tried using Dictionary
, HybridDictionary
, and Hashtable
for this but I always get the following error when calling ConfigFile.Load()
or ConfigFile.Save()
:
我现在想存储的驱动器号关联数组映射(关键是驱动器盘符,价值是网络路径)和使用都试过Dictionary
,HybridDictionary
和Hashtable
这个,但是打电话时,我总是得到下面的错误ConfigFile.Load()
或者ConfigFile.Save()
:
There was an error reflecting type 'App.ConfigFile'. [snip] System.NotSupportedException: Cannot serialize member App.Configfile.mappedDrives [snip]
反映类型“App.ConfigFile”时出现错误。[snip] System.NotSupportedException:无法序列化成员 App.Configfile.mappedDrives [snip]
From what I've read Dictionaries and HashTables can be serialized, so what am I doing wrong?
从我读过的内容来看,字典和哈希表可以被序列化,那么我做错了什么?
[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();
}
}
}
}
采纳答案by bruno conde
You can't serialize a class that implements IDictionary. Check out this link.
您不能序列化实现 IDictionary 的类。查看此链接。
Q: Why can't I serialize hashtables?
A: The XmlSerializer cannot process classes implementing the IDictionary interface. This was partly due to schedule constraints and partly due to the fact that a hashtable does not have a counterpart in the XSD type system. The only solution is to implement a custom hashtable that does not implement the IDictionary interface.
问:为什么我不能序列化哈希表?
答:XmlSerializer 无法处理实现 IDictionary 接口的类。这部分是由于计划限制,部分是由于散列表在 XSD 类型系统中没有对应的事实。唯一的解决方案是实现一个不实现 IDictionary 接口的自定义哈希表。
So I think you need to create your own version of the Dictionary for this. Check this other question.
所以我认为你需要为此创建自己的字典版本。检查这个其他问题。
回答by David Schmitt
Dictionaries and Hashtables are not serializable with XmlSerializer
. Therefore you cannot use them directly. A workaround would be to use the XmlIgnore
attribute to hide those properties from the serializer and expose them via a list of serializable key-value pairs.
字典和哈希表不能用XmlSerializer
. 因此你不能直接使用它们。一种解决方法是使用该XmlIgnore
属性从序列化程序中隐藏这些属性,并通过可序列化的键值对列表公开它们。
PS: constructing an XmlSerializer
is very expensive, so always cache it if there is a chance of being able to re-use it.
PS:构建一个XmlSerializer
非常昂贵,所以如果有机会重用它,总是缓存它。
回答by Nissim
This article explains exactly how to handle this: How do I... Serialize a hash table in C# when the application requires it?
这篇文章详细解释了如何处理这个问题: 当应用程序需要时,我如何...在 C# 中序列化一个哈希表?
I hope this is helpful
我希望这是有帮助的
回答by osman pirci
There is a solution at Paul Welter's Weblog - XML Serializable Generic Dictionary
Paul Welter 的博客中有一个解决方案- XML Serializable Generic Dictionary
For some reason, the generic Dictionary in .net 2.0 is not XML serializable. The following code snippet is a xml serializable generic dictionary. The dictionary is serialzable by implementing the IXmlSerializable interface.
出于某种原因,.net 2.0 中的通用 Dictionary 不是 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
}
回答by Jean-Philippe Gravel
You should explore Json.Net, quite easy to use and allows Json objects to be deserialized in Dictionary directly.
您应该探索 Json.Net,它非常易于使用,并允许直接在 Dictionary 中反序列化 Json 对象。
example:
例子:
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
回答by Despertar
Instead of using XmlSerializer
you can use a System.Runtime.Serialization.DataContractSerializer
. This can serialize dictionaries and interfaces no sweat.
而不是使用XmlSerializer
您可以使用System.Runtime.Serialization.DataContractSerializer
。这可以毫不费力地序列化字典和接口。
Here is a link to a full example, http://theburningmonk.com/2010/05/net-tips-xml-serialize-or-deserialize-dictionary-in-csharp/
这是一个完整示例的链接,http://theburningmonk.com/2010/05/net-tips-xml-serialize-or-deserialize-dictionary-in-csharp/
回答by user2921681
Create a serialization surrogate.
创建序列化代理。
Example, you have a class with public property of type Dictionary.
例如,您有一个具有 Dictionary 类型的公共属性的类。
To support Xml serialization of this type, create a generic key-value class:
要支持这种类型的 Xml 序列化,请创建一个通用的键值类:
public class SerializeableKeyValue<T1,T2>
{
public T1 Key { get; set; }
public T2 Value { get; set; }
}
Add an XmlIgnore attribute to your original property:
将 XmlIgnore 属性添加到您的原始属性:
[XmlIgnore]
public Dictionary<int, string> SearchCategories { get; set; }
Expose a public property of array type, that holds an array of SerializableKeyValue instances, which are used to serialize and deserialize into the SearchCategories property:
公开一个数组类型的公共属性,该属性包含一个 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 );
}
}
}
回答by Keyo
I wanted a SerializableDictionary class that used xml attributes for key/value so I've adapted Paul Welter's class.
我想要一个 SerializableDictionary 类,它使用 xml 属性作为键/值,所以我改编了 Paul Welter 的类。
This produces xml like:
这会产生 xml,如:
<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]);
}
}
}
}
回答by Saikrishna
the Dictionary class implements ISerializable. The definition of Class Dictionary given below.
Dictionary 类实现 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
I don't think that is the problem. refer to the below link, which says that if you are having any other data type which is not serializable then Dictionary will not be serialized. http://forums.asp.net/t/1734187.aspx?Is+Dictionary+serializable+
我不认为这是问题所在。参考下面的链接,它说如果您有任何其他不可序列化的数据类型,那么 Dictionary 将不会被序列化。 http://forums.asp.net/t/1734187.aspx?Is+Dictionary+serializable+
回答by Wojtpl2
You can use ExtendedXmlSerializer. If you have a class:
您可以使用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>();
}
}
and create instance of this class:
并创建此类的实例:
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");
You can serialize this object using ExtendedXmlSerializer:
您可以使用 ExtendedXmlSerializer 序列化此对象:
ExtendedXmlSerializer serializer = new ExtendedXmlSerializer();
var xml = serializer.Serialize(config);
Output xml will look like:
输出 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>
You can install ExtendedXmlSerializer from nugetor run the following command:
您可以从nuget安装 ExtendedXmlSerializer或运行以下命令:
Install-Package ExtendedXmlSerializer
Here is online example
这是在线示例