2016-11-29 88 views
3

我有一个使用protobuf-net进行继承的具体用例,我还没有找到它在这里覆盖(尽管很乐意被重定向到任何有用的答案) 。protobuf-net:deserialise将关键字扩展到继承层次结构

我必须从第三方protobuf源(GTFS-RT)反序列化一些对象,并且提供的.proto使用关键字extend来扩展基类型(我们也使用其他提要),这从客观角度来看似乎是明智的。但是,我无法获得protobuf-net将这种格式的提要反序列化为适当的继承层次结构。

例如,碱.proto限定FeedHeader(被叫transit_realtime包下):

message FeedHeader { 
    required string gtfs_realtime_version = 1; 

    enum Incrementality { 
    FULL_DATASET = 0; 
    DIFFERENTIAL = 1; 
    } 
    optional Incrementality incrementality = 2 [default = FULL_DATASET]; 

    optional uint64 timestamp = 3; 

    extensions 1000 to 1999; 
} 

第三方延伸FeedHeader到包括另一属性:

extend transit_realtime.FeedHeader { 
optional NyctFeedHeader nyct_feed_header = 1001; 
} 

我想将其解串为以下类的层次结构:

namespace Base.GTFS 
{ 
    [ProtoContract(Name = nameof(FeedHeader))] 
    public class FeedHeader 
    { 
     [ProtoMember(1, IsRequired = true, Name = nameof(gtfs_realtime_version), DataFormat = DataFormat.Default)] 
     public string gtfs_realtime_version { get; set; } 

     [ProtoMember(2, IsRequired = false, Name = nameof(incrementality), DataFormat = DataFormat.TwosComplement)] 
     [DefaultValue(Incrementality.FULL_DATASET)] 
     public Incrementality incrementality { get; set; } = Incrementality.FULL_DATASET; 

     [ProtoMember(3, IsRequired = false, Name = nameof(timestamp), DataFormat = DataFormat.TwosComplement)] 
     [DefaultValue(default(ulong))] 
     public ulong timestamp { get; set; } = default(ulong); 

     public FeedHeader() { } 

     #region Nested Enums 
     [ProtoContract(Name = nameof(Incrementality))] 
     public enum Incrementality 
     { 

      [ProtoEnum(Name = nameof(FULL_DATASET), Value = 0)] 
      FULL_DATASET = 0, 

      [ProtoEnum(Name = nameof(DIFFERENTIAL), Value = 1)] 
      DIFFERENTIAL = 1 
     } 
     #endregion 
    } 
} 

namespace Other.GTFS 
{ 
    [ProtoContract(Name = nameof(FeedHeader))] 
    public class FeedHeader : Base.GTFS.FeedHeader 
    { 
     /// <summary> 
     /// NYCT Subway extensions for the feed header 
     /// </summary> 
     [ProtoMember(1001, Name = nameof(nyct_feed_header), IsRequired = false, DataFormat = DataFormat.Default)] 
     public NyctFeedHeader nyct_feed_header { get; set; } = null; 

     public FeedHeader() : base() { } 
    } 
} 

通过阅读其他帖子在这里和其他地方后,我试图使用AddSubType方法和AddSurrogate方法,但发现我只能可靠地让所有字段反序列化,如果我覆盖基类中的所有字段。这看起来非常低效,并且如果(和何时)基本类型改变将会中断。我们还需要将serialise用于其他提要的基本类型,所以我需要一个易于扩展的解决方案。

有谁知道任何方式来支持这种情况下,或有任何建议可能有帮助吗?

回答

0

proto2extendextensions关键字不对应于继承层次结构。相反,它们更接近于c#中的partial关键字,允许在一个文件中部分定义消息,并在另一个文件中添加。来自proto2 language documentation from Google

使用扩展名可以声明消息中的一系列字段号可用于第三方扩展。其他人可以在自己的.proto文件中使用这些数字标签为消息类型声明新字段,而无需编辑原始文件。

因此,如果我创建一个包含文件Question40863857_1.proto

package transit_realtime; 

message FeedHeader { 
    required string gtfs_realtime_version = 1; 

    enum Incrementality { 
    FULL_DATASET = 0; 
    DIFFERENTIAL = 1; 
    } 
    optional Incrementality incrementality = 2 [default = FULL_DATASET]; 

    optional uint64 timestamp = 3; 

    extensions 1000 to 1999; 
} 

而且Question40863857_2.proto包含:

import "Question40863857_1.proto"; 

// Some random enum since the NyctFeedHeader type wasn't included in the question. 
enum NyctFeedHeader { 
    Value0 = 0; 
    Value1 = 1; 
} 

extend transit_realtime.FeedHeader { 
optional NyctFeedHeader nyct_feed_header = 1001; 
} 

然后自动生成使用从他们的交流#类protobuf网的protogen.exe utility如下:

protogen.exe -i:Question40863857_1.proto -i:Question40863857_2.proto 

所得类型生成:

// Generated from: Question40863857_1.proto 
namespace transit_realtime 
{ 
    [global::System.Serializable, global::ProtoBuf.ProtoContract([email protected]"FeedHeader")] 
    public partial class FeedHeader : global::ProtoBuf.IExtensible 
    { 
    public FeedHeader() {} 

    private string _gtfs_realtime_version; 
    [global::ProtoBuf.ProtoMember(1, IsRequired = true, [email protected]"gtfs_realtime_version", DataFormat = global::ProtoBuf.DataFormat.Default)] 
    public string gtfs_realtime_version 
    { 
     get { return _gtfs_realtime_version; } 
     set { _gtfs_realtime_version = value; } 
    } 
    private transit_realtime.FeedHeader.Incrementality _incrementality = transit_realtime.FeedHeader.Incrementality.FULL_DATASET; 
    [global::ProtoBuf.ProtoMember(2, IsRequired = false, [email protected]"incrementality", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] 
    [global::System.ComponentModel.DefaultValue(transit_realtime.FeedHeader.Incrementality.FULL_DATASET)] 
    public transit_realtime.FeedHeader.Incrementality incrementality 
    { 
     get { return _incrementality; } 
     set { _incrementality = value; } 
    } 
    private ulong _timestamp = default(ulong); 
    [global::ProtoBuf.ProtoMember(3, IsRequired = false, [email protected]"timestamp", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] 
    [global::System.ComponentModel.DefaultValue(default(ulong))] 
    public ulong timestamp 
    { 
     get { return _timestamp; } 
     set { _timestamp = value; } 
    } 
    private NyctFeedHeader _nyct_feed_header = NyctFeedHeader.Value0; 
    [global::ProtoBuf.ProtoMember(1001, IsRequired = false, [email protected]"nyct_feed_header", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)] 
    [global::System.ComponentModel.DefaultValue(NyctFeedHeader.Value0)] 
    public NyctFeedHeader nyct_feed_header 
    { 
     get { return _nyct_feed_header; } 
     set { _nyct_feed_header = value; } 
    } 
    [global::ProtoBuf.ProtoContract([email protected]"Incrementality")] 
    public enum Incrementality 
    { 

     [global::ProtoBuf.ProtoEnum([email protected]"FULL_DATASET", Value=0)] 
     FULL_DATASET = 0, 

     [global::ProtoBuf.ProtoEnum([email protected]"DIFFERENTIAL", Value=1)] 
     DIFFERENTIAL = 1 
    } 

    private global::ProtoBuf.IExtension extensionObject; 
    global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing) 
     { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); } 
    } 

} 
// Generated from: Question40863857_2.proto 
// Note: requires additional types generated from: Question40863857_1.proto 
namespace Question40863857_2 
{ 
    [global::ProtoBuf.ProtoContract([email protected]"NyctFeedHeader")] 
    public enum NyctFeedHeader 
    { 

     [global::ProtoBuf.ProtoEnum([email protected]"Value0", Value=0)] 
     Value0 = 0, 

     [global::ProtoBuf.ProtoEnum([email protected]"Value1", Value=1)] 
     Value1 = 1 
    } 

} 

公告不存在继承层次,只是一个单一的类FeedHeader与来自两个.proto文件领域,具有与辅助枚举沿。

事实上,如果您有一套完整的.proto文件,您可以使用protogen.exe为您生成c#类型,从而避免这种困难。或者,使用视觉工作室的plugin

反之,如果需要检查,如果通过C#类型T规定的合同相匹配的要求.proto文件,你可以这样做:

Console.WriteLine(RuntimeTypeModel.Default.GetSchema(typeof(T))); 

typeof(Other.GTFS.FeedHeader)这会导致:

message FeedHeader { 
    optional NyctFeedHeader nyct_feed_header = 1001 [default = Value0]; 
} 
enum NyctFeedHeader { 
    Value0 = 0; 
    Value1 = 1; 
} 

这显然不是你想要的。

+0

感谢您的信息 - 我认为这几乎是如何工作的。这是一个耻辱,因为我有一个服务层位于声明一个使用基类的接口之上 - 我想我需要将基础模型的必需部分提取到接口中,并确保它们得到应用到部分生成的类(更容易看到,因为它们是部分)。 – davide