“已经添加了具有相同键的项”;protobuf-net错误

本文关键字:错误 protobuf-net 添加 | 更新日期: 2023-09-27 18:12:53

我正在尝试用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);
            }
        }
    }
}
}

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

我如何通过对代码的最小更改来克服它?

亲切的问候,Yossi .

“已经添加了具有相同键的项”;protobuf-net错误

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

[ProtoContract(SkipConstructor = true)]

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

[ProtoBeforeDeserialization]
private void Foo()
{
    Dict.Clear();
}

第三种选择是使用:

[ProtoContract(SkipConstructor = true)]

:

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

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