"具有相同键的项目已添加"与protobuf-net的错误

yos*_*sic 6 c# dictionary protocol-buffers protobuf-net

我正在尝试用Marc Gravell的C#替换现有的序列化程序和protobuf.我的代码很广泛,我的目标是能够以最小的变化进行切换.

我遇到了一个问题,我相信我理解它为什么会发生,但需要帮助克服 - 特别是一个解决方案,需要对我现有的代码和类进行最少的更改.我的代码很复杂,所以我创建了以下简短示例来演示问题:

using System;
using System.Collections.Generic;
using System.IO;
using ProtoBuf;


namespace ConsoleApplication1
{
    class program_issue
    {

    [ProtoContract]
    public class Father
    {
        public Father()
        {
            sonny = new Son();
        }

        [ProtoMember(101)]
        public string Name;

        [ProtoMember(102)]
        public Son sonny;

    }

    [ProtoContract]
    public class Son
    {
        public Son()
        {
            Dict.Add(10, "ten");
        }

        [ProtoMember(103)]
        public Dictionary<int, string> Dict = new Dictionary<int, string>();
    }


    static void Main(string[] args)
    {
        Father f1 = new Father();
        f1.Name = "Hello";
        byte[] bts = PBSerializer.Serialize(typeof(Father), f1);

        Father f2;
        PBSerializer.Deserialize(bts, out f2);

    }


    public static class PBSerializer
    {
        public static byte[] Serialize(Type objType, object obj)
        {
            MemoryStream stream = new MemoryStream();
            ProtoBuf.Serializer.Serialize(stream, obj);
            string s = Convert.ToBase64String(stream.ToArray());
            byte[] bytes = stream.ToArray();
            return bytes;
        }


        public static void Deserialize(byte[] data, out Father obj)
        {
            using (MemoryStream stream = new MemoryStream(data))
            {
                obj = ProtoBuf.Serializer.Deserialize<Father>(stream);
            }

        }
    }

}
}
Run Code Online (Sandbox Code Playgroud)

简而言之,当创建父对象时,它会创建一个子对象,该对象在具有某些值的字典中.我假设当protobuf在反序列化时尝试重建对象时使用相同的构造函数(因此也启动带有值的字典),然后尝试再次推送相同的值作为反序列化 - >错误的一部分.

如何通过对代码进行最少的更改来克服它?

亲切的问候,Yossi.

Mar*_*ell 7

这里最简单的选择可能是:

[ProtoContract(SkipConstructor = true)]
Run Code Online (Sandbox Code Playgroud)

正如它所说,它将不执行构造函数(或字段初始化程序).请注意,如果没有数据,这将使字典保持为空.另一种方法可能是使用序列化回调(在加载数据之前触发):

[ProtoBeforeDeserialization]
private void Foo()
{
    Dict.Clear();
}
Run Code Online (Sandbox Code Playgroud)

第三种选择是通过使用以下方式组合上述内容:

[ProtoContract(SkipConstructor = true)]
Run Code Online (Sandbox Code Playgroud)

和:

[ProtoAfterDeserialization]
private void Foo()
{
    if(Dict == null) Dict = new Dictionary<int,string>();
}
Run Code Online (Sandbox Code Playgroud)

即使没有数据,也要将其默认为空字典.请注意,您也需要执行此操作Father,因为它使用默认Son构造函数.