使用 ProtoBuf-net 对派生类型(字典)进行解脱处理不会正确设置对象字段
本文关键字:处理 字段 对象 设置 解脱 派生 ProtoBuf-net 类型 字典 使用 | 更新日期: 2023-09-27 18:26:01
我正在尝试序列化然后反序列化一个对象,该对象的类派生自具有字符串成员字段的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");
}
}
这种效应可能与 protobuf 中 IDictionary 类型的序列化的内部实现有关。您可以在字典中添加版本数据或重写您的 dto 对象,如此示例所示。如果使用此数据对象,它将修复测试:
[ProtoContract]
public class TempClass
{
[ProtoMember(1)]
public Dictionary<string, int> data;
[ProtoMember(2)]
public string Version;
public TempClass() { }
}
第三种方法是编写自己的序列化。
[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;
}
}
使用[ProtoContract(UseProtoMembersOnly = true, IgnoreListHandling = true)]
而不是[ProtoContract]
为我解决了问题。
这将阻止 protobuf-net 使用字典规则序列化类。(见此(
下面是一个示例。
[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);
}
(这些代码仅为示例(