包含 Dictionary 成员的 Serialize 类

在我的 之前的问题的基础上,我决定(反)序列化我的配置文件类,它工作得很好。

我现在想要存储一个关联数组的驱动器号来映射(key 是驱动器号,value 是网络路径) ,并且尝试过使用 DictionaryHybridDictionaryHashtable,但是在调用 ConfigFile.Load()或者 ConfigFile.Save()时总是会出现以下错误:

有一个错误反射类型 ‘ App. ConfigFile’ 异常: 不能 序列化成员 应用程序

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

[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();
}
}
}
}
173987 次浏览

字典和哈希表不能用 XmlSerializer序列化。因此,您不能直接使用它们。解决办法是使用 XmlIgnore属性从序列化程序隐藏这些属性,并通过可序列化的键-值对列表公开它们。

PS: 构建一个 XmlSerializer是非常昂贵的,所以如果有机会可以重用它,总是缓存它。

不能序列化实现 IDictionary 的类。

问: 为什么我不能序列化散列表?

答: XmlSerializer 无法处理 实现 IDictionary 的类 这部分是由于 时间限制和部分原因 事实上,哈希表没有 在 XSD 类型中有一个对应物 系统。唯一的解决办法是 实现一个自定义哈希表 没有实现字典 接口。

所以我认为你需要为此创建你自己版本的字典。

本文将详细解释如何处理这个问题: 当应用程序需要时,如何在 C # 中序列化散列表?

我希望这能有所帮助

Paul Welter 的 Weblog-XML 可序列化通用词典有一个解决方案

由于某种原因,在。Net 2.0不是可序列化的 XML。下面的代码片段是一个可序列化的 xml 通用字典。该字典可通过实现 IXmlSerializer 接口进行序列化。

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
}

您应该探索 Json.Net,它非常容易使用,并且允许直接在 Dictionary 中反序列化 Json 对象。

詹姆斯 · 牛顿金

例如:

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

你可以使用一个 System.Runtime.Serialization.DataContractSerializer来代替使用 XmlSerializer,这样可以很容易的串行化字典和接口。

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

创建序列化代理项。

例如,您有一个类,该类具有 Dictionary 类型的 public 属性。

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

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

我想要一个 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]);
}
}
}
}

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

我觉得这不是问题所在。请参考下面的链接,该链接指出,如果您有任何其他不可序列化的数据类型,则 Dictionary 将不会被序列化。 Http://forums.asp.net/t/1734187.aspx?is+dictionary+serializable+

你可以使用 ExtendedXmlSerializer 扩展 XmlSerializer。 如果你有课:

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

这里是 网上例子

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

Https://learn.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;
}

基于对 Paul Welter 博客的参考,这里有一个与最新的 C # 兼容的更新代码集,可以处理可空类型(因为,你知道,我很擅长在没有警告的情况下编写代码!):

/*-----------------------------------------------------------------------------------------------------
* Class borrowed from Paul Welter's blog https://weblogs.asp.net/pwelter34/444961
* Thank you for your invaluable contribution!
*/


using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;


namespace {Your-Namespace}
{


[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue>
: Dictionary<TKey, TValue>, IXmlSerializable
where TKey : notnull
{
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(typeof(TKey));
XmlSerializer valueSerializer = new(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(typeof(TKey));
XmlSerializer valueSerializer = new(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
}
}