«Элемент с таким же ключом уже добавлен» ошибка с protobuf-net - PullRequest
6 голосов
/ 01 ноября 2011

Я пытаюсь заменить существующий сериализатор на protobuf для C # Марка Грэйвела.Мой код обширный, и моя цель - сделать так, чтобы переключение было минимальным.

Я столкнулся с проблемой, которая, я полагаю, я понимаю, почему это происходит, но мне нужна помощь в преодолении - особенно решение, которое потребовало быНаименьшие изменения в моем уже существующем коде и классах.Мой код сложен, поэтому я создал следующий короткий пример, чтобы продемонстрировать проблему:

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 пытается перестроить объект при десериализации, он использует тот же конструктор (таким образом, также инициируя словарь со значениями), а затем пытается выдвинуть те же значения снова как часть десериализации -> error.Как я могу преодолеть это с минимальными изменениями в моем коде?

С уважением, Йосси.

1 Ответ

7 голосов
/ 01 ноября 2011

Самый простой вариант здесь, вероятно:

[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 по умолчанию.

...