Protobuf.net - все поля класса должны быть сериализованы - PullRequest
0 голосов
/ 29 ноября 2018

Вот пример класса , предоставленный Марком Гравелем в его введении о том, как использовать Protobuf.net:

[ProtoContract]
class Person {
    [ProtoMember(1)]
    public int Id {get;set;}
    [ProtoMember(2)]
    public string Name {get;set;}
    [ProtoMember(3)]
    public Address Address {get;set;}
}
[ProtoContract]
class Address {
    [ProtoMember(1)]
    public string Line1 {get;set;}
    [ProtoMember(2)]
    public string Line2 {get;set;}
}

У меня есть несколько вопросов, на которые я не смог найти ответыпосле поиска в Интернете:

  1. Если в первый день я знаю, что мне не нужно свойство name [ProtoMember (2)], то если я опущу [ProtoMember (2))], Protobut.net будет игнорировать это свойство и не включать его в выходные данные сериализации?Если это правда, то когда данные десериализованы на другом конце - для чего инициализируется имя - ноль?

  2. Позволяет сказать, что все 3 свойства изначально сериализуются, как показано выше.Если в будущем выяснится, что свойство name [ProtoMember (2)] больше не требуется, можно ли безопасно пропустить атрибут [ProtoMember (2)], чтобы сериализовались только первое и третье свойства?Если это правда, будет ли хорошо просто оставить номера атрибутов, как показано (т.е. 1 и 3)?Любые предостережения, если это так?

  3. Если можно опустить атрибут сериализации для свойства в классе, то что произойдет, если определение класса на стороне десериализации выходит за рамкисинхронизировать?Например, скажем, что класс десериализации определяет все 3 свойства выше, а код сериализации определяет только 1 и 3?Точно так же, если код десериализации ожидает увидеть только свойства 1 и 3, но код сериализации отправляет все 3, это все еще будет работать или будет выдавать ошибку?

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

Поскольку другие могут захотеть узнать ответ на эти вопросы, я решил опубликовать вопрос и поделиться своими выводами.

На самом деле эти вопросы довольно легко решить с помощью тестовой программы:

class Program
{
    static void Main(string[] args)
    {
        var person = new Person1
        {
            Id = 12345,
            Name = "Fred",
            Address = new Address
            {
                Line1 = "Flat 1",
                Line2 = "The Meadows"
            }
        };
        //
        byte[] arr = Serialize(person);
        Person2 newPerson = Deserialize(arr);
        /*
        using (var file = File.Create("person.bin"))
        {
            Serializer.Serialize(file, person);
        }
        //
        Person newPerson;
        using (var file = File.OpenRead("person.bin"))
        {
            newPerson = Serializer.Deserialize<Person>(file);
        }
        */
    }

    public static byte[] Serialize(Person1 person)
    {
        byte[] result;
        using (var stream = new MemoryStream())
        {
            Serializer.Serialize(stream, person);
            result = stream.ToArray();
        }
        return result;
    }

    public static Person2 Deserialize(byte[] tData)
    {
        using (var ms = new MemoryStream(tData))
        {
            return Serializer.Deserialize<Person2>(ms);
        }
    }
}

[ProtoContract]
class Person1
{
    [ProtoMember(1)]
    public int Id { get; set; }
    [ProtoMember(2)]
    public string Name { get; set; }
    [ProtoMember(3)]
    public Address Address { get; set; }
}
[ProtoContract]
class Address
{
    [ProtoMember(1)]
    public string Line1 { get; set; }
    [ProtoMember(2)]
    public string Line2 { get; set; }
}

[ProtoContract]
class Person2
{
    [ProtoMember(1)]
    public int Id { get; set; }
    [ProtoMember(2)]
    public string Name { get; set; }
    [ProtoMember(3)]
    public Address Address { get; set; }
}

Итак, чтобы проверить, будет ли Protobuf.net игнорировать свойство Name при сериализации, сначала запустите код в режиме отладки, чтобы увидеть общее количество байтов со всеми 3 сериализованными свойствами.Это можно сделать, установив точку останова в строке Serialize (person) и изучив размер arr.

Затем удалите атрибут [ProtoMember (2)] из свойства Name класса Person1.Выполнение кода в отладке показывает, что он пропускает его, потому что число байтов на 6 меньше, чем раньше.Когда объект затем десериализуется обратно в объект Person2, он затем показывает, что свойство Name инициализируется как null.

После замены атрибута [ProtoMember (2)] для свойства Name в классе Person1,удалите этот же атрибут для класса Person2.После отладки кода он показывает, что после вызова Deserialize свойство Person2.Name имеет значение null.

Итак, похоже, что Protobuf.net был хорошо спроектирован, чтобы быть достаточно гибким, эффективным иНекоторые способы обратно совместимы в том смысле, что они поддерживают удаление устаревших свойств.

0 голосов
/ 29 ноября 2018
  1. правильно;с точки зрения того, к чему он инициализируется - это обычно зависит от вашего типа, поэтому в данном случае (поскольку ваш тип не инициализирует его), да: null (примечание: есть также опцияв котором конструктор может быть подавлен, и в этом случае это будет null , даже если ваш класс имеет конструктор / инициализатор)
  2. да, это нормально (и ожидается)
  3. добавление и удаление свойств ожидается и нормально, поэтому библиотека прощает либо;при получении неожиданных полей дальнейшие действия зависят от того, реализует ли ваш класс IExtensible (часто путем подкласса Extensible) - если это так, непредвиденные данные хранятся отдельно, так что их можно запрашивать вручную или (болееобщие) «сработало в раунде» (т. е. если вы снова сериализуете его, дополнительные данные сохранятся, даже если вы этого не ожидали)
  4. (да, я знаю, вы не спрашивали 4) - библиотекатакже поддерживает «условную сериализацию», где свойства by-by могут быть опущены в зависимости от условий - например, public bool ShouldSerializeName() => Name != null && SomethingElse == 42;
...