2016-11-29 91 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