protobuf-net:deserialise将关键字扩展到继承层次结构

时间:2016-11-29 10:44:49

标签: c# inheritance protobuf-net

我有一个使用protobuf-net进行继承的特定用例,我还没有找到它(尽管很高兴被重定向到任何有用的答案)。

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

例如,基础.proto定义了extend(在名为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方法,但发现如果我覆盖所有字段,我只能可靠地将所有字段反序列化基类中的字段。这似乎非常低效,并且如果(和何时)基类型发生变化将会中断。我们还需要对其他feed的基本类型使用serialise,因此我需要一个易于扩展的解决方案。

有没有人知道支持这种情况的任何方法,或者有任何可能有帮助的建议?

1 个答案:

答案 0 :(得分:0)

proto2 extendextensions关键字与继承层次结构不对应。相反,它们更接近于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-net的protogen.exe utility从它们自动生成c#类,如下所示:

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

生成结果类型:

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

    private string _gtfs_realtime_version;
    [global::ProtoBuf.ProtoMember(1, IsRequired = true, Name=@"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, Name=@"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, Name=@"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, Name=@"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(Name=@"Incrementality")]
    public enum Incrementality
    {

      [global::ProtoBuf.ProtoEnum(Name=@"FULL_DATASET", Value=0)]
      FULL_DATASET = 0,

      [global::ProtoBuf.ProtoEnum(Name=@"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(Name=@"NyctFeedHeader")]
    public enum NyctFeedHeader
    {

      [global::ProtoBuf.ProtoEnum(Name=@"Value0", Value=0)]
      Value0 = 0,

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

}

请注意,没有继承层次结构,只有一个类FeedHeader,包含两个.proto文件中的字段,以及辅助枚举。

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

相反,如果您需要检查c#type 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;
}

这显然不是你想要的。