Protobuf.net-需要序列化的类的所有字段

时间:2018-11-29 09:04:29

标签: c# protobuf-net

这里是example class provided by Marc Gravel的简介,介绍如何使用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. 如果在第1天,我知道我不需要名称属性[ProtoMember(2)],那么如果我省略[ProtoMember(2)]属性,Protobut.net将忽略该属性,并且不在输出序列化数据中包括它?如果为true,则在另一端反序列化数据时-Name初始化为什么-null?

  2. 让我们说所有3个属性都已如上所述初步序列化。如果将来发现不再需要名称属性[ProtoMember(2)],可以安全地省略[ProtoMember(2)]属性,以便仅对第一个和第三个属性进行序列化吗?如果为true,是否可以仅保留显示的属性编号(即1和3)?如果是这种情况,有什么警告吗?

  3. 如果可以为类中的属性省略序列化属性,那么如果反序列化方面的类定义不同步怎么办?例如,假设反序列化类定义了上面的所有3个属性,但是序列化代码仅定义了1和3?同样,如果反序列化代码只希望看到属性1和3,而序列化代码将全部发送3,那么它仍然可以工作还是会产生错误?

2 个答案:

答案 0 :(得分:2)

  1. 正确;关于初始化的内容-通常取决于您的类型,因此在这种情况下(因为您的类型未初始化),是的:null(注意:还有一个可以抑制构造函数的选项,在这种情况下,即使您的班级有构造函数/初始化程序,它也会是null
  2. 是的,这很好(并且可以预期)
  3. 属性的添加和删除是正常的,因此该库也可以原谅。当收到意外字段时,下一步将取决于您的类是否实现IExtensible(通常通过子类Extensible的实现)-如果可以,则意外数据将单独存储,以便仍可以手动查询,或(更常见的)“往返”(即,如果您再次对其进行序列化,则即使您没有想到,也会保留额外的数据)
  4. (是的,我知道您没有问4)-该库还支持“条件序列化”,其中可以根据条件省略by-by属性-例如public bool ShouldSerializeName() => Name != null && SomethingElse == 42;

答案 1 :(得分:1)

由于其他人可能想知道这些问题的答案,所以我决定发布问题并分享我的发现。

这些问题实际上很容易通过测试程序解决:

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属性,请首先在debug中运行代码以查看所有3个属性序列化的字节总数。这可以通过在Serialize(person)行上设置断点并检查arr的大小来完成。

然后,从Person1类的Name属性中删除[ProtoMember(2)]属性。在调试中运行代码表明它已将其排除在外,因为字节数比以前少了6个。然后将该对象反序列化为Person2的对象时,它表明Name属性被初始化为null。

在将Person1类的Name属性替换为[ProtoMember(2)]属性后,请为Person2类删除相同的属性。通过代码调试之后,它表明在反序列化调用之后,Person2.Name属性设置为null。

因此,看起来Protobuf.net经过精心设计,具有很高的灵活性,效率,并且在某些方面向后兼容,因为它支持删除过时的属性。