一、问题描述
在使用C#对字典容器Dictionary<TKey, TValue>内容进行XML序列化报错【System.Exception:“不支持类型 System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],因为它实现 IDictionary。”】如下所示:
或反序列化操作时会报如下错误【System.Exception:“不支持类型 System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],因为它实现 IDictionary。”】如下图所示
二、问题分析
通过异常信息可以看到是由于XML序列化或反序列化程序导致报错,且该错误是由于IDictionary导致,通过反编译查看【System.Xml.Serialization.TypeScope】内容可以发现代码直接对IDictionary类型直接做返回异常处理,不让序列化或反序列化。
三、解决方法
1、创建一个新的类【DictionaryEx】,继承Dictionary<TKey, TValue>类和IXmlSerializable接口:
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace Kernal
{
#region 可序列化字典类 + public class DictionaryEx<TKey, TValue>
/// <summary>
/// 可序列化字典类
/// </summary>
/// <typeparam name="TKey">键泛型</typeparam>
/// <typeparam name="TValue">值泛型</typeparam>
[System.Serializable]
public class DictionaryEx<TKey, TValue>
: Dictionary<TKey, TValue>, IXmlSerializable
{
#region 构造函数
#region 默认构造函数 + public DictionaryEx()
/// <summary>
/// 默认构造函数
/// </summary>
public DictionaryEx()
: base()
{
}
#endregion
#region 构造函数 + public DictionaryEx(int capacity)
/// <summary>
/// 构造函数
/// </summary>
/// <param name="capacity">可包含的初始元素数</param>
public DictionaryEx(int capacity)
: base(capacity)
{
}
#endregion
#region 构造函数 + public DictionaryEx(IEqualityComparer<TKey> comparer)
/// <summary>
/// 构造函数
/// </summary>
/// <param name="comparer">比较键时要使用的 比较器 实现,或者为 null,以便为键类型使用默认的 比较器</param>
public DictionaryEx(IEqualityComparer<TKey> comparer)
: base(comparer)
{
}
#endregion
#region 构造函数 + public DictionaryEx(IDictionary<TKey, TValue> dictionary)
/// <summary>
/// 构造函数
/// </summary>
/// <param name="dictionary">初始数据</param>
public DictionaryEx(IDictionary<TKey, TValue> dictionary)
: base(dictionary)
{
}
#endregion
#region 构造函数 + public DictionaryEx(int capacity, IEqualityComparer<TKey> comparer)
/// <summary>
/// 构造函数
/// </summary>
/// <param name="capacity">可包含的初始元素数</param>
/// <param name="comparer">比较键时要使用的 比较器 实现,或者为 null,以便为键类型使用默认的 比较器</param>
public DictionaryEx(int capacity, IEqualityComparer<TKey> comparer)
: base(capacity, comparer)
{
}
#endregion
#region 构造函数 + public DictionaryEx(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
/// <summary>
/// 构造函数
/// </summary>
/// <param name="dictionary">初始数据</param>
/// <param name="comparer">比较键时要使用的 比较器 实现,或者为 null,以便为键类型使用默认的 比较器</param>
public DictionaryEx(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
: base(dictionary, comparer)
{
}
#endregion
#endregion
#region 取得概要 + public XmlSchema GetSchema()
/// <summary>
/// 取得概要
/// 注:根据MSDN的文档,此方法为保留方法,一定返回 null。
/// </summary>
/// <returns>Xml概要</returns>
public XmlSchema GetSchema()
{
return null;
}
#endregion
#region 从 XML 对象中反序列化生成本对象 + public void ReadXml(XmlReader reader)
/// <summary>
/// 从 XML 对象中反序列化生成本对象
/// </summary>
/// <param name="reader">包含反序列化对象的 XmlReader 流</param>
public void ReadXml(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 != 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();
}
#endregion
#region 将本对象序列化为 XML 对象 + public void WriteXml(XmlWriter writer)
/// <summary>
/// 将本对象序列化为 XML 对象
/// </summary>
/// <param name="writer">待写入的 XmlWriter 对象</param>
public void WriteXml(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
}
#endregion
}
2、将原来使用Dictionary的类替换为DictionaryEx类
/// <summary>
/// 保存语言信息
/// </summary>
private bool SaveFileExpandNameList(DictionaryEx<string,string> languageInfoDic, string languageFilePathAndName)
{
bool success = false;
if (languageInfoDic != null && languageInfoDic.Count >= 0)
{
//将对象序列化为xml字符串
string strXml = XmlHelper.ObjectToXml2(languageInfoDic);
if (!string.IsNullOrEmpty(strXml))
{
//先清空文件
FileOPC.Clear(languageFilePathAndName);
//保存Json字符串文件(覆盖写入)
success = FileOPC.OverWrite(languageFilePathAndName, strXml);
}
}
return success;
}
/// <summary>
/// 获取语言信息
/// </summary>
/// <returns>返回语言信息字典</returns>
private DictionaryEx<string, string> GetLanguageInfo(string languageFilePathAndName)
{
if (string.IsNullOrEmpty(languageFilePathAndName)) return null;
DictionaryEx<string, string> languageInfoDic = new DictionaryEx<string, string>();
//读取xml文件字符串
string strXml = FileOPC.Read(languageFilePathAndName);
//读取xml文件并序列化为对象
languageInfoDic = XmlHelper.XmlToObject2<DictionaryEx<string, string>>(strXml);
return languageInfoDic;
}
3、序列化DictionaryEx<TKey, TValue>容器内容的XML内容如下所示: