Сериализация и десериализация объектов, которые реализуют абстрактные классы с использованием Protobuf-net - PullRequest
0 голосов
/ 28 апреля 2018

Фон

Я пытаюсь получить приведенный ниже класс для сериализации и десериализации класса, который реализует абстрактный класс, используя Protobuf-net . Однако происходит сбой при десериализации с ошибкой: «Не найден конструктор без параметров для AbstractTest».

Что я делаю неправильно?

Что я уже пробовал

Я потратил много времени на поиски ответа, но очень мало вопросов связано с абстрактной природой моего вопроса.

Минимальный пример

    public class TestRunner
    {
        public void Go()
        {
            string encoded = Serialize<ConcreteTest>(new ConcreteTest());

            object ob = Deserialize(new System.IO.MemoryStream(Encoding.ASCII.GetBytes(encoded)));
        }

        public static string Serialize<T>(T data)
        {
            MemoryStream outputStream = new MemoryStream();
            Serializer.Serialize<T>(outputStream, data);
            outputStream.Position = 0;

            StreamReader outputReader = new StreamReader(outputStream);
            return outputReader.ReadToEnd();
        }

        public static object Deserialize(Stream data)
        {
             AbstractTest test = Serializer.Deserialize<AbstractTest>(data);
             //Error from the line above: No parameterless constructor found for AbstractTest

            if (test.ID == 3)
            {
                 return Serializer.Deserialize<ConcreteTest>(data);
            }

            throw new Exception("We don't know how to handle the message type if I got here!");
        }
    }


[ProtoBuf.ProtoContract]
[ProtoBuf.ProtoInclude(25, typeof(ConcreteTest))]
public abstract class AbstractTest
{
    public AbstractTest()
    {

    }

    [ProtoBuf.ProtoMember(1)]
    public int ID = 3;

    public abstract void Test();
}


[ProtoBuf.ProtoContract]
public class ConcreteTest : AbstractTest
{
    [ProtoBuf.ProtoMember(2)]
    public int ID2 = 4;
    public override void Test()
    {
         MasterLog.DebugWriteLine("It worked!");
    }
}

1 Ответ

0 голосов
/ 28 апреля 2018

Первоначальная проблема заключается в том, что вы повредили данные, используя Encoding в обратном направлении. Encoding преобразует произвольный текст в отформатированный двоичный файл (что означает: отформатированный в соответствии с правилами этого «кодирования») и отформатированный двоичный файл обратно в произвольный текст. Он предназначен для описания того, как записать текст в поток. Вы использовали его задом наперед, что означает: вы использовали его для преобразования произвольного двоичного файла в другом формате в строку. Возможно, вы также использовали два разных кодирования (StreamReader почти наверняка не будет использовать ASCII по умолчанию).

Итак: вы не можете использовать Encoding здесь. То, что вы можете использовать - это base-64; например:

using (var outputStream = new MemoryStream())
{
    Serializer.Serialize<T>(outputStream, data);

    return Convert.ToBase64String(outputStream.GetBuffer(),
        0, (int)outputStream.Length);
}

Вы можете изменить это, используя Convert.FromBase64String:

using (var ms = new System.IO.MemoryStream(Convert.FromBase64String(encoded)))
{
    object ob = Deserialize(ms);
    Console.WriteLine(ob.ToString());
}

Существует также проблема, когда вы дважды десериализуете поток без перемотки, что означает, что вторая десериализация работает с нулевыми байтами, что означает, что будет пытаться создать экземпляр абстрактного типа; поэтому вам также необходимо исправить метод Deserialize:

public static object Deserialize(Stream data)
{
    AbstractTest test = Serializer.Deserialize<AbstractTest>(data);

    return test;
}

После этого все работает правильно.

...