2013-02-25 61 views
1

我使用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 <字符串對象序列化一個'List ' >'。我將添加一個完整的代碼段來演示這個問題。 – zimdanen 2013-02-25 17:46:59

回答

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(); 
+0

謝謝!我在'key'和'value'節點上添加了這個類型作爲屬性,這可以讓我序列化/反序列化,而不會出現問題。 – zimdanen 2013-02-25 19:07:31

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

這將序列化,但不會反序列化。您需要在這些新行中寫出類型,並在反序列化中使用該類型。根據您以前的評論,在編輯時發佈我的最終代碼。 – zimdanen 2013-02-25 21:29:26

+0

Zimdanen你的代碼在'public void WriteXml(System.Xml.XmlWriter writer)'方法中拋出了一個錯誤'我測試了這個方法,並提供了一個修復程序,我沒有意識到你的代碼的反序列化部分有錯誤。 – MethodMan 2013-02-25 23:45:52

相關問題