Недопустимое поведение ProtoBuf-net при сериализации объекта, содержащего список - PullRequest
0 голосов
/ 08 сентября 2018

Мне трудно объяснить поведение в следующем примере:

[ProtoContract]
public class Class1Proto
{
    [ProtoMember(1)]
    public int data1 = 1;
    [ProtoMember(2)]
    public string data2 = "MYRANDOMSTRING";
}

[ProtoContract]
public class ProtoChunk
{
    [ProtoMember(1)]
    public List<Class1Proto> arr = new List<Class1Proto>();

    public const int PageSize = 4096;
}

Использование:

    byte[] page = new byte[ProtoChunk.PageSize];

    ProtoChunk originalData = new ProtoChunk();
    for (int i = 0; i < 100; i++)
    {
        Class1Proto p = new Class1Proto();
        p.data1 = i * 2;
        p.data2 = (i * 2).ToString();
        originalData.arr.Add(p);
    }

    using (var memStream = new MemoryStream(page, writable:true))
    {
        Serializer.SerializeWithLengthPrefix(memStream, originalData, PrefixStyle.Fixed32);
    }

    using (var memStream = new MemoryStream(page, writable:false))
    {
        ProtoChunk deserializedData = Serializer.DeserializeWithLengthPrefix<ProtoChunk>(memStream, PrefixStyle.Fixed32);
    }

Я ожидаю, что originalData и deserializedData должны быть идентичны. И они в основном за исключением того, что deserializedData.arr[0].data1 == 1 while originalData.arr[0].data1 == 0. Все остальные объекты идентичны, даже включая originalData.arr[0].data2 and deserializedData.arr[0].data2 (строковое поле).

1 Ответ

0 голосов
/ 09 сентября 2018

protobuf-net предполагает «неявные значения по умолчанию, равные нулю» - т. Е. Если не указано иное, члены имеют значение по умолчанию, равное нулю, что означает: ноль не передается. Это не является чисто произвольным - на самом деле это спецификация «proto3» (ну ... более или менее; в «proto3» ноль - это only разрешенное значение по умолчанию).

Ваш код, в частности инициализатор свойства, действует так, как если бы он имел значение по умолчанию 1, поэтому: когда ноль не передается, 1 все еще применяется вашим конструктором, и это становится значением (десериализация в protobuf является операцией «слияния» - сохраняются уже существующие значения, опять же в соответствии со спецификацией).

Параметры:

  • сообщить protobuf-net о вашем значении по умолчанию - добавьте [DefaultValue(1)] к свойству
  • говорит protobuf-net не запускать конструктор (и инициализатор свойства) - добавьте SkipConstructor = true к [ProtoContract]
  • скажите protobuf-net, чтобы он не принимал такое поведение: RuntimeTypeModel.Default.ImplicitZeroDefault = false;
  • добавьте свои собственные условные обратные вызовы сериализации (могу привести пример, если вы действительно хотите)

Я бы лично выбрал первый вариант.

...