使用对象串行器对列表进行序列化

使用对象串行器对列表进行序列化

问题描述:

我使用this blog entry中定义的SerializableDictionary来存储<string, object>数据并将它传递给WCF服务或从其传递。如果我使用值类型作为值,这可以很好地工作,因为它们可以很容易变成object s。不过,如果我使用类似使用对象串行器对列表进行序列化

new List<int>() { 5, 10 },我得到一个异常:

类型System.Collections.Generic.List`1 [[System.Int32,mscorlib程序,版本= 4.0.0.0,文化= 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(); 

SOLUTION

由于尼科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个静态列表,以防止intantiating他们到处都是。为了能够防止在每个节点上写出类型,我必须在开始时写出所提供的类型。

+0

zimdanen是你试图通过/分配数组.. int?是否有你在这里显示的错字'新列表(){5,10}'哪里是'=' – MethodMan 2013-02-25 17:41:29

+0

@DJKRAZE:我试图使用SerializableDictionary ' >'。我将添加一个完整的代码段来演示这个问题。 – zimdanen 2013-02-25 17:46:59

序列化的问题是(解)序列化程序需要知道如何处理对象。 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(); 
+0

谢谢!我在'key'和'value'节点上添加了这个类型作为属性,这可以让我序列化/反序列化,而不会出现问题。 – zimdanen 2013-02-25 19:07:31

在类

您在链接引用请执行下列操作

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 
    } 
+0

这将序列化,但不会反序列化。您需要在这些新行中写出类型,并在反序列化中使用该类型。根据您以前的评论,在编辑时发布我的最终代码。 – zimdanen 2013-02-25 21:29:26

+0

Zimdanen你的代码在'public void WriteXml(System.Xml.XmlWriter writer)'方法中抛出了一个错误'我测试了这个方法,并提供了一个修复程序,我没有意识到你的代码的反序列化部分有错误。 – MethodMan 2013-02-25 23:45:52