我使用this blog entry中定义的SerializableDictionary
来存储<string, object>
数据,并将其传递给WCF服务或从WCF服务传递。如果我使用值类型作为值,这可以正常工作,因为它们可以很容易地装箱成为object
。但是,如果我使用像
new List<int>() { 5, 10 }
,我得到一个例外:
类型System.Collections.Generic.List`1 [[System.Int32,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089]]可能不会在此上下文中使用。
根据讨论here,我应该使用value.GetType()
初始化XmlSerializer
;然而,虽然这让我序列化,但我不知道如何以一般方式反序列化回SerializableDictionary
。
我不确定是否有办法彻底改变它,同时仍允许<string, object>
作为我的类型参数 - 我可以将值序列化为二进制而不是XML并以这种方式传输(相同的代码是序列化和反序列化,所以我不关心互操作性),但我想尽可能使用XML。
修改
完整代码示例:
XmlSerializer serializer = new XmlSerializer(typeof(SerializableDictionary<string, object>));
SerializableDictionary<string, object> dic = new SerializableDictionary<string, object>();
dic["test"] = new List<int>() { 5, 10 };
StringBuilder sb = new StringBuilder();
XmlWriter writer = XmlWriter.Create(sb);
serializer.Serialize(writer, dic);
string ser = sb.ToString();
解
感谢 Nico Schertler 为我提供了正确的答案。我在这里发布我的最终代码,万一有人需要它。这与第一个链接中的原始代码向后兼容,因此该代码序列化的任何内容都可以通过以下方式反序列化。
[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{
#region " IXmlSerializable Members "
#region " WriteXml "
public void WriteXml(XmlWriter writer)
{
// Base types
string baseKeyType = typeof(TKey).AssemblyQualifiedName;
string baseValueType = typeof(TValue).AssemblyQualifiedName;
writer.WriteAttributeString("keyType", baseKeyType);
writer.WriteAttributeString("valueType", baseValueType);
foreach (TKey key in this.Keys)
{
// Start
writer.WriteStartElement("item");
// Key
Type keyType = key.GetType();
XmlSerializer keySerializer = GetTypeSerializer(keyType.AssemblyQualifiedName);
writer.WriteStartElement("key");
if (keyType != typeof(TKey)) { writer.WriteAttributeString("type", keyType.AssemblyQualifiedName); }
keySerializer.Serialize(writer, key);
writer.WriteEndElement();
// Value
TValue value = this[key];
Type valueType = value.GetType();
XmlSerializer valueSerializer = GetTypeSerializer(valueType.AssemblyQualifiedName);
writer.WriteStartElement("value");
if (valueType != typeof(TValue)) { writer.WriteAttributeString("type", valueType.AssemblyQualifiedName); }
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
// End
writer.WriteEndElement();
}
}
#endregion
#region " ReadXml "
public void ReadXml(XmlReader reader)
{
bool wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
{
return;
}
// Base types
string baseKeyType = typeof(TKey).AssemblyQualifiedName;
string baseValueType = typeof(TValue).AssemblyQualifiedName;
while (reader.NodeType != XmlNodeType.EndElement)
{
// Start
reader.ReadStartElement("item");
// Key
XmlSerializer keySerializer = GetTypeSerializer(reader["type"] ?? baseKeyType);
reader.ReadStartElement("key");
TKey key = (TKey)keySerializer.Deserialize(reader);
reader.ReadEndElement();
// Value
XmlSerializer valueSerializer = GetTypeSerializer(reader["type"] ?? baseValueType);
reader.ReadStartElement("value");
TValue value = (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
// Store
this.Add(key, value);
// End
reader.ReadEndElement();
reader.MoveToContent();
}
reader.ReadEndElement();
}
#endregion
#region " GetSchema "
public XmlSchema GetSchema()
{
return null;
}
#endregion
#endregion
#region " GetTypeSerializer "
private static readonly Dictionary<string, XmlSerializer> _serializers = new Dictionary<string, XmlSerializer>();
private static readonly object _deadbolt = new object();
private XmlSerializer GetTypeSerializer(string type)
{
if (!_serializers.ContainsKey(type))
{
lock (_deadbolt)
{
if (!_serializers.ContainsKey(type))
{
_serializers.Add(type, new XmlSerializer(Type.GetType(type)));
}
}
}
return _serializers[type];
}
#endregion
}
我只是写出类型,如果它与基类型不同,以便保持XML的长度,我保留一个XmlSerializer
的静态列表,以防止对它们进行实例化到处都是。我必须在开头写出提供的类型,以便能够防止在每个节点上写出类型。
答案 0 :(得分:3)
序列化的问题是(解)序列化程序需要知道如何处理对象。 object
的序列化程序不知道如何序列化List<int>
。
对于序列化,您已经在问题中给出了答案。使用value.GetType()
确定值的类型。此外,您必须保存类型本身。使用类型(type.AssemblyQualifiedName
)的字符串表示可以轻松实现这一点。
TValue value = this[key];
var type = value.GetType();
XmlSerializer valueSerializer = new XmlSerializer(type);
writer.WriteStartElement("type");
writer.WriteString(type.AssemblyQualifiedName);
//you can use FullName if you don't need to address external libraries
writer.WriteEndElement();
writer.WriteStartElement("content");
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
对于反序列化,您需要读取类型并反序列化值:
reader.ReadStartElement("value");
reader.ReadStartElement("type");
var typename = reader.ReadContentAsString();
reader.ReadEndElement();
var type = Type.GetType(typename);
XmlSerializer valueSerializer = new XmlSerializer(type);
reader.ReadStartElement("content");
TValue value = (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadEndElement();
答案 1 :(得分:0)
在您在链接中引用的类中执行以下操作
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Collections;
namespace sampleLogin
{
[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];
var type = value.GetType();//new line added here
valueSerializer = new XmlSerializer(type);//New line added here
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
writer.WriteEndElement();
}
}
#endregion
}