使用 ProtoBuf-net 反序列化派生类型(字典)未正确设置对象字段
Posted
技术标签:
【中文标题】使用 ProtoBuf-net 反序列化派生类型(字典)未正确设置对象字段【英文标题】:Using ProtoBuf-net to deseriailize a derived type (dictionary) does not properly set object fields 【发布时间】:2014-10-08 16:43:50 【问题描述】:我正在尝试序列化然后反序列化其类派生自带有字符串成员字段的Dictionary<string,int>
的对象。
public class TempClass : Dictionary<string, int>
public string Version;
public TempClass()
我编写了一个单元测试来捕获我遇到的问题:使用 protobuf-net 序列化或反序列化(到/从字节 [])时未设置成员字段。当验证反序列化的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");
【问题讨论】:
感谢您提供简单易用的测试。这样可以节省时间。 【参考方案1】:这个效果可能与protobuf中IDictionary类型序列化的内部实现有关。 您可以在字典中添加版本数据或像此示例一样重写您的 dto 对象。 如果您使用此数据对象,它将修复您的测试:
[ProtoContract]
public class TempClass
[ProtoMember(1)]
public Dictionary<string, int> data;
[ProtoMember(2)]
public string Version;
public TempClass()
第三种方式是自己写序列化。
【讨论】:
如果TempClass
不必也是IEnumerable
、IDictionary
等,这将起作用。但是,更改TempClass
的类型会破坏很多现有代码,所以除非我也实现Dictionary
的所有接口,否则此解决方案将不起作用。
您不需要实现接口,您可以在序列化之前将数据包装到 dto 对象。
事实仍然是,如果我在单元测试中使用您的类版本,那么代码将无法编译。我需要编译 AND 的代码通过测试。【参考方案2】:
[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;
【讨论】:
您已修改测试以通过新的实现。这违反了最初编写单元测试以强制执行的合同。我正在寻找原始问题的答案。不是您自己问题的答案。 哦..太好了。你要求解决方案 - 我给你一个方法。你要代码——我给你一个代码。比你投反对票。所以,我认为这对某些人会有帮助。【参考方案3】:使用[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);
(这些代码仅供参考)
【讨论】:
以上是关于使用 ProtoBuf-net 反序列化派生类型(字典)未正确设置对象字段的主要内容,如果未能解决你的问题,请参考以下文章
需要在运行时确定哪些类型 protobuf-net 可以序列化/反序列化