用于继承的 Protobuf-net .proto 文件生成

Posted

技术标签:

【中文标题】用于继承的 Protobuf-net .proto 文件生成【英文标题】:Protobuf-net .proto file generation for inheritance 【发布时间】:2010-09-15 12:18:40 【问题描述】:

我正在对 Protobuf-net 进行原型设计,以替换我们当前使用 [Datacontract] 将对象序列化为 Xml 的一些现有 C# 代码。

使用 protobuffer,我们可以轻松地与 Java 共享数据。因此,我对 Protobuf-net 的 .proto 文件生成非常感兴趣。这对我迄今为止测试的几乎所有用例都有效。

但现在有了继承,情况就不同了。为继承类生成的 .proto 文件非常简单 - 不包括基类的任何字段。

继承本身在 C# 中运行良好 - 我可以读取生成的字节流(请参阅下面的测试)。所以内部二进制流包含了基类的所有字段

生成的.proto:

message ProtoScholar 
   optional string Subject = 1;

如果我能理解字节流是如何写出的,我可以手动创建相应的 .proto 文件。

有人有使用 protobuf-net 创建 .proto 文件以进行继承的经验吗?

任何关于如何为继承创建数据流的信息都会很棒。

我的数据模型如下:

[DataContract]
[ProtoInclude(7, typeof(ProtoScholar))]
public class ProtoAlien


    [DataMember(Order = 1)]
    public string Name
    
        get;
        set;
    


    [DataMember(Order = 2)]
    public double? Average
    
        get;
        set;
    

    [DataMember(Order = 3)]
    public int? HowMuch
    
        get;
        set;
    

    [DataMember(Order = 4, IsRequired = true)]
    public Height Length
    
        get; set;
    


    [DataMember(Order = 5, IsRequired = true)]       
    public Character Personality
    
        get;
        set;
    

    [DataMember(Order = 6, IsRequired = true)]       
    public DateTime When
    
        get; set;
    

    public enum Height
    
        Short = 1,
        Medium,
        Tall
    

    public enum Character : long
    
        Wasp = 1717986918,
        BumbleBee,
        WorkerBee,
        Hornet,
        Queen
            


[DataContract()]
public class ProtoScholar : ProtoAlien


    [DataMember(Order=1)]
    public string Subject
    
        get; set;
    


我的 NUnit 测试如下所示:

[Test]
    public void TestInheritanceSupport()
    

        var protoBuffer = new ProtoScholar
        
            Name = "Ben11",
            HowMuch = null,
            Length = ProtoAlien.Height.Tall,
            Personality = ProtoAlien.Character.WorkerBee,
            When = new DateTime(2010, 4, 1, 2, 33, 56, 392),
            Subject = "Alien Economics"
        ;


        using (var ms = new MemoryStream())
        

            var proto = Serializer.GetProto<ProtoScholar>();
            Console.WriteLine(proto);

            //Serialize to a Memory Stream
            Serializer.Serialize(ms, protoBuffer);

            Console.WriteLine(ms.Length);
            ms.Position = 0;
            var deserializedProtoBuffer = Serializer.Deserialize<ProtoScholar>(ms);

            Assert.AreEqual("Ben11", deserializedProtoBuffer.Name);

            Assert.Null(deserializedProtoBuffer.HowMuch);
            Assert.AreEqual(ProtoAlien.Height.Tall, deserializedProtoBuffer.Length);

            Assert.AreEqual(ProtoAlien.Character.WorkerBee, deserializedProtoBuffer.Personality);
            Assert.AreEqual(new DateTime(2010, 4, 1, 2, 33, 56, 392), deserializedProtoBuffer.When);
            Assert.AreEqual("Alien Economics", deserializedProtoBuffer.Subject);

        

    

【问题讨论】:

【参考方案1】:

由于继承不是核心规范的一部分,我基本上使用封装来表示这一点。所以你的[ProtoInclude] 映射到:

message ProtoAlien 
   // other fields 1 thru 6 [snip]
   optional ProtoScholar ProtoScholar = 7;

message ProtoScholar 
   optional string Subject = 1;

GetProto&lt;T&gt;() 在 v2 中正在进行大修,因此它应该支持更多这些场景。

【讨论】:

好的,我试试看。在没有核心规范的情况下,封装方法是有意义的。但是将基类封装到继承类中不是更有意义吗? 确实!从 .proto 文件生成 java 代码,它正在工作。 @André 在一般情况下,这会产生一些问题,无法正确地将事物表示为其子/超类型。即如果道具是SomeBaseType,并且你想给它一个SomeSuperType 实例。从超类型封装,我们只知道消息结构方面的SomeBaseType。所以继承不会在线上工作。

以上是关于用于继承的 Protobuf-net .proto 文件生成的主要内容,如果未能解决你的问题,请参考以下文章

protobuf-net 中日期时间的 .proto 消息是啥

如何使用 protobuf-net 处理 .proto 文件

如何在 Protobuf-net 中动态添加 Proto 成员

.proto 文件中带有 oneof 的 Protobuf-net

protobuf-net - 反引号、字典和 .proto 文件

从 protobuf-net 中的自定义 RuntimeTypeModel 生成 .proto 文件