2014-10-08 21 views
2

我想序列化,然後反序列化一個對象的類從Dictionary<string,int>從字符串成員字段派生。使用ProtoBuf淨去派生類型(字典)deseriailize沒有正確設置對象字段

public class TempClass : Dictionary<string, int> 
    { 
     public string Version; 

     public TempClass() { } 
    } 

我寫一個單元測試,以捕獲我運行到該問題:所述構件字段未設置時或者串行化或反串行化(到/從一個字節[])與protobuf網。驗證已反序列化的Version已正確設置時,此測試在最後的Assert中失敗。它始終設置爲null而不是正確的"someVersion"

[TestClass] 
    public class serializationTest 
    { 
     [TestMethod] 
     public void TestMethod1() 
     { 
      string newVersion = "someVersion"; 

      TempClass original = new TempClass() 
      { 
       {"a", 2}, 
       {"b", 3}, 
       {"c", 1}, 
      }; 
      original.Version = newVersion; 

      byte[] serialized = Serialize(original); 

      TempClass deserialized = Deserialize(serialized); 

      // Validate 
      foreach (var pair in original) 
      { 
       Assert.IsTrue(deserialized.ContainsKey(pair.Key)); 
       Assert.AreEqual(pair.Value, deserialized[pair.Key]); 
      } 

      Assert.AreEqual(newVersion, original.Version, "original mapping version not set correctly"); 
      Assert.AreEqual(newVersion, deserialized.Version, "deserialized version doesn't match"); 
     } 

     private static TempClass Deserialize(byte[] serialized) 
     { 
      TempClass deserialized; 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       ms.Write(serialized, 0, serialized.Length); 
       ms.Position = 0; 
       deserialized = Serializer.Deserialize<TempClass>(ms); 
      } 
      return deserialized; 
     } 

     private static byte[] Serialize(TempClass mapping) 
     { 
      byte[] serialized; 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       Serializer.Serialize(ms, mapping); 
       serialized = ms.ToArray(); 
      } 
      return serialized; 
     } 
    } 

我已經嘗試與BinaryFormatter,也是DataContractSerializer同樣的工作都無濟於事。有人可以幫我找出導致此測試失敗的原因嗎?

的後續問題:相反,如果我重新定義了TempClass這樣,構造函數總是被調用,而不是正確地設置成員字段原來的Version。我如何反序列化沒有構造函數創建一個新的Version,而只是複製原始的?

public class TempClass : Dictionary<string, int> 
    { 
     public string Version; 

     public TempClass() 
     { 
      Version = DateTime.UtcNow.ToString("s"); 
     } 
    } 
+0

感謝您輕鬆使用測試。這節省了時間。 – gabba 2014-10-08 17:20:38

回答

0

可能是這種效應與protobuf中IDictionary類型的內部實現有關。 您可以在字典中添加版本數據,或者像本示例一樣重寫您的對象。 如果你使用這個數據對象,這將解決您的測試:

[ProtoContract] 
public class TempClass 
{ 
    [ProtoMember(1)] 
    public Dictionary<string, int> data; 

    [ProtoMember(2)] 
    public string Version; 

    public TempClass() { } 
} 

第三種方式是寫自己的序列。

+0

如果'TempClass'不一定也是'IEnumerable','IDictionary'等,那麼這會起作用。但是,改變'TempClass'的類型會破壞很多現有的代碼,所以這個解決方案將不會工作,除非我也實現了'Dictionary'的所有接口。 – Shamster 2014-10-08 17:29:34

+0

你不需要實現接口,你可以在序列化之前將你的數據封裝到dto對象中。 – gabba 2014-10-08 17:32:32

+0

事實上,如果我在單元測試中使用您的類的版本,那麼代碼不會編譯。我需要編譯** AND **通過測試的代碼。 – Shamster 2014-10-08 17:39:26

-1
[ProtoContract] 
public class TempClass 
{ 
    [ProtoMember(1)] 
    public Dictionary<string, int> data; 

    [ProtoMember(2)] 
    public string Version; 

    public TempClass() { } 
} 

[TestClass] 
public class serializationTest 
{ 
    [TestMethod] 
    public void TestMethod1() 
    { 
     string newVersion = "someVersion"; 

     TempClass original = new TempClass() 
     { 
      data = new Dictionary<string,int> 
      { 
       {"a", 2}, 
       {"b", 3}, 
       {"c", 1}, 
      }, 
      Version = newVersion 
     }; 

     byte[] serialized = Serialize(original); 

     TempClass deserialized = Deserialize(serialized); 

     // Validate 
     foreach (var pair in original.data) 
     { 
      Assert.IsTrue(deserialized.data.ContainsKey(pair.Key)); 
      Assert.AreEqual(pair.Value, deserialized.data[pair.Key]); 
     } 

     Assert.AreEqual(newVersion, original.Version, "original mapping version not set correctly"); 
     Assert.AreEqual(newVersion, deserialized.Version, "deserialized version doesn't match"); 
    } 

    private static TempClass Deserialize(byte[] serialized) 
    { 
     TempClass deserialized; 
     using (MemoryStream ms = new MemoryStream()) 
     { 
      ms.Write(serialized, 0, serialized.Length); 
      ms.Position = 0; 
      deserialized = Serializer.Deserialize<TempClass>(ms); 
     } 
     return deserialized; 
    } 

    private static byte[] Serialize(TempClass mapping) 
    { 
     byte[] serialized; 
     using (MemoryStream ms = new MemoryStream()) 
     { 
      Serializer.Serialize(ms, mapping); 
      serialized = ms.ToArray(); 
     } 
     return serialized; 
    } 
} 
+0

您已經修改了測試以便通過新的實施。這打破了單元測試最初被寫入執行的合同。我正在尋找原始問題的答案。不是你自己問題的答案。 – Shamster 2014-10-08 17:42:44

+0

哦..太棒了。你要求解決辦法 - 我給你一個辦法。你問代碼 - 我給你一個代碼。比你低調。 所以,我以任何方式對此都有幫助。 – gabba 2014-10-08 17:47:19

0

使用[ProtoContract(UseProtoMembersOnly = true, IgnoreListHandling = true)] 而不是[ProtoContract]爲我解決了這個問題。

這將阻止protobuf-net使用字典規則序列化類。 (見this

這裏是一個例子。

[ProtoContract(UseProtoMembersOnly = true, IgnoreListHandling = true)] 
public class ProtobufTest<T, P> : IDictionary<T, P> 
{ 
    [ProtoMember(1)] 
    private readonly Dictionary<T, P> _dataset; 

    [ProtoMember(2)] 
    public string Name; 

    private ProtobufTest(Dictionary<T, P> dataset, string name) 
    { 
     _dataset = dataset ?? new Dictionary<T, P>(); 
     Name = name; 
    } 
    public ProtobufTest(string name) : this(new Dictionary<T, P>(), name) { } 
    private ProtobufTest() : this(null, string.Empty) {} 

    // 
    // IDictionary implementation is omitted. 
    // 
} 

這裏是一個示例單元測試。

[Test] 
    public void ProtobufTestNameSerializeDeserialize() 
    { 
     ProtobufTest<double, double> t = new ProtobufTest<double, double>("233"); 
     ProtobufTest<double, double> d; 
     using (MemoryStream ms = new MemoryStream()) 
     { 
      Serializer.SerializeWithLengthPrefix(ms, t, PrefixStyle.Base128); 
      ms.Position = 0; 
      d = Serializer.DeserializeWithLengthPrefix<ProtobufTest<double, double>>(ms, PrefixStyle.Base128); 
     } 
     Assert.AreEqual(t.Name, d.Name); 
    } 

(這些代碼示例只)