protobuf-net не десериализует массив sbyte - PullRequest
1 голос
/ 19 августа 2010

У меня есть класс, определенный в C ++ / CLI:

literal Int32 BufferLength = 4000;

Message()
{
   num1 = 0;
   num2 = 0;
   str1 = String::Empty;
   buffer = gcnew array<System::SByte>(BufferLength);
};

[ProtoMember(1)]
property double num1
{
   double get() { return _num1; }
   void set(double value) { _num1 = value; }
}

[ProtoMember(2)]
property double num2
{
   double get() { return _num2; }
   void set(double value) { _num2 = value; }
}

[ProtoMember(3)]
property String^ str1
{
   String^ get() { return _str1; }
   void set(String^ value) { _str1 = value; }
}

[ProtoMember(4)]
property array<System::SByte>^ buffer
{
   array<System::SByte>^ get() { return _buffer; }
   void set(array<System::SByte>^ value) { _buffer = value; }
}

Во время отладки я вижу, как сериализатор извлекает значение из свойства буфера с тактовыми данными.Когда запускается десериализатор, я вижу, что он помещает данные в свойство буфера, однако массив заполняется нулями вместо данных, которые были там до сериализации.Я попытался установить IsRequired = true в атрибуте ProtoMember, не повезло.

У меня есть другие сообщения, определенные с массивами sbyte, которые хорошо десериализуются.Однако эти массивы довольно короткие (максимум 10).Единственное, что выделяется здесь для меня, это длина этого массива.Помогите!: -)

Редактировать: Думаю, я должен также упомянуть, что я использую v1 r282.

1 Ответ

1 голос
/ 20 августа 2010

Ошибка здесь в том, что protobuf-net (в соответствии с protobuf-spec) десериализует данные списка (и т. Д.) Путем добавления данных.И в «v1» (версия, которую вы используете) он всегда запускает конструктор.Поэтому при десериализации он запускает конструктор (создает массив длиной 4000), затем обрабатывает данные и добавляет еще 4000 элементов.Если вы проверите, то увидите, что массив теперь имеет длину 8000 (вы будете рады узнать, что он не меняет размер массива 4000 раз ...).

Исправления:

  • в v2 (на самом деле не совсем выпущено, это только для информации) вы можете полностью подавить конструктор
  • или просто удалить создание массива в ctor, и вместо этого назначить через набор (возможно, добавивстатический метод фабрики, который делает это для вас, чтобы сэкономить некоторый код)

Следующая тестовая установка (для удобства переведенная на C #, извините) работает нормально:

using System;
using System.Diagnostics;
using ProtoBuf;

namespace ConsoleApplication28
{
    class Program
    {
        static void Main()
        {
            var msg = Message.Create();
            var rand = new Random();
            var buffer = msg.buffer;
            for (int i = 0; i < buffer.Length; i++)
                buffer[i] = (sbyte)rand.Next(-128, 128);
            var clone = Serializer.DeepClone(msg);
            var cloneBuffer = clone.buffer;
            Debug.Assert(!ReferenceEquals(buffer, cloneBuffer), "Should be different buffer");

            Debug.Assert(buffer.Length == cloneBuffer.Length, "Should be same length");
            for(int i = 0 ; i < buffer.Length ; i++)
                Debug.Assert(buffer[i] == cloneBuffer[i], "Should be same value at index " + i);
        }
    }

    [ProtoContract]
    public class Message
    {
        const int BufferLength = 4000;
        public static Message Create()
        {
            var msg = new Message();
            msg.buffer = new sbyte[BufferLength];
            return msg;
        }
        private Message()
        {
           num1 = 0;
           num2 = 0;
           str1 = String.Empty;
        }


        private double _num1, _num2;
        private string _str1;
        private sbyte[] _buffer;

        [ProtoMember(1)]
        public double num1
        {
            get { return _num1; }
            set { _num1 = value; }
        }

        [ProtoMember(2)]
        public double num2
        {
           get { return _num2; }
           set { _num2 = value; }
        }

        [ProtoMember(3)]
        public String str1
        {
           get { return _str1; }
           set { _str1 = value; }
        }

        [ProtoMember(4)]
        public sbyte[] buffer
        {
           get { return _buffer; }
           set { _buffer = value; }
        }    
    }
}
...