使用 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;
        }
    }

我已经尝试了与BinaryFormatterDataContractSerializer相同的工作,但无济于事。有人可以帮我找出导致此测试失败的傻瓜吗?

后续问题:如果我像这样重新定义TempClass,则始终调用构造函数,而不是将成员字段正确设置为原始Version。如何在不构造函数创建新Version而只复制原始的情况下反序列化?

    public class TempClass : Dictionary<string, int>
    {
        public string Version;
        public TempClass() 
        { 
            Version = DateTime.UtcNow.ToString("s");
        }
    }

使用 ProtoBuf-net 对派生类型(字典)进行解脱处理不会正确设置对象字段

这种效应可能与 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);
    }

(这些代码仅为示例(