我有一个使用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,因此我需要一个易于扩展的解决方案。
有没有人知道支持这种情况的任何方法,或者有任何可能有帮助的建议?
答案 0 :(得分:0)
proto2 extend
和extensions
关键字与继承层次结构不对应。相反,它们更接近于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;
}
这显然不是你想要的。