如何让protobuf-net和protostuff相互支持.Net和Java中的继承类?

时间:2013-09-02 02:50:25

标签: c# java protobuf-net protostuff

我在Windows系统和Android设备上进行基于.Net的程序之间的通信。在.Net端,我正在使用Marc Gravell的伟大的protobuf-net程序,而在Android端,我正在使用David Yu的伟大的protostuff程序。

我的程序(到目前为止)是使用.Net类作为定义类。我使用protobuf-net Serializer.GetProto()方法生成.proto文件,使用protostuff的protostuff-compiler程序来生成与.Net类相对应的Java类,或多或少。

这似乎工作得很好,除了我遇到了继承问题。是的,我知道,继承不适用于协议缓冲区。但是protobuf-net和protostuff都以自己的方式实现了对继承类的支持。

所以我的问题是,是否有人建议使用简单的方法将继承的C#类映射到继承的Java类,反之亦然?

这是我正在使用的一个例子。这些是C#类:

   public /* abstract */ class ProgramInfoBase
   {
      private string _programName;

      private string _programVersion;

      [ProtoMember(1)]
      public string ProgramName
      {
         get { return _programName; }
         set { _programName = value; }
      }

      [ProtoMember(2)]
      public string ProgramVersion
      {
         get { return _programVersion; }
         set { _programVersion = value; }
      }
   }


   public class ProgramInfoAndroid : ProgramInfoBase
   {
      private string _androidDeviceName;


      [ProtoMember(1)]
      public string AndroidDeviceName
      {
         get { return _androidDeviceName; }
         set { _androidDeviceName = value; }
      }
   }


   public class ProgramInfoWindows : ProgramInfoBase
   {
      private string _windowsMachineName;

      [ProtoMember(1)]
      public string WindowsMachineName
      {
         get { return _windowsMachineName; }
         set { _windowsMachineName = value; }
      }
   }

这是我的.proto文件之一:

package Merlinia.MessagingDefinitions;

option java_package = "com.Merlinia.MMessaging_Test.protostuff";

message ProgramInfoAndroid {
   optional string AndroidDeviceName = 1;
}
message ProgramInfoBase {
   optional string ProgramName = 1;
   optional string ProgramVersion = 2;
   // the following represent sub-types; at most 1 should have a value
   optional ProgramInfoAndroid ProgramInfoAndroid = 1001;
   optional ProgramInfoWindows ProgramInfoWindows = 1002;
}
message ProgramInfoWindows {
   optional string WindowsMachineName = 1;
}

通过protostuff的protostuff-compiler程序运行它会生成三个独立的Java类,这是可以预期的。但我想要的是让它为Java类生成相应的C#类继承,并为protobuf-net和protostuff之间的序列化和反序列化生成,以支持两端的继承类。

编辑:

我现在改变主意了。请参阅以下问题:How to get protobuf-net to flatten and unflatten inherited classes in .Net?

1 个答案:

答案 0 :(得分:2)

首先,请注意,protobuf规范中未定义多态性;任何实施都是定制的。如果他们是相同的话会很好。

基本上,看起来他们采用了根本不同的范式; protobuf-net将子类型视为从基类型向下开始的嵌套对象,根据您发布的.proto(由于熟悉的注释,我假设它来自GetProto)。这个选择有很多原因,包括:

  • 数据可以由没有任何继承概念的实现建模和解析
  • 数据中没有类型/名称/元/等依赖关系(这是protobuf避免在其他地方使用的东西,所以保持这个很好)
  • 还有助于提供平台之间的完全可移植性
  • 数据存活类型重构
  • 如果读者/作者应用程序处于不同的级别,客户端仍然可以读取 知道的数据,即使编写者添加了一个新的子类,它没有t 了解
  • 适用于任意字段,合并等(只要两个合并不在不同的继承分支中)
然而,protostuff以不同的方式做事;看一下repo,它将类型名称写入字段127(并将数据字段限制为126),并使用该名称执行类型解析。我会猜测 未经测试 )这意味着在.proto术语中,架构是:

message ProgramInfoBase {
   optional string ProgramName = 1;
   optional string ProgramVersion = 2;
   required string MagicTypeName = 127;
}
message ProgramInfoWindows {
   optional string ProgramName = 1;
   optional string ProgramVersion = 2;
   optional string WindowsMachineName = 3;
   required string MagicTypeName = 127;
}
message ProgramInfoAndroid {
   optional string ProgramName = 1;
   optional string ProgramVersion = 2;
   optional string AndroidDeviceName = 3;
   required string MagicTypeName = 127;
}

所以,此时你有几个选择:

  • 以不使用继承的方式重新构建两个实现中的数据 - 所以你有一个扁平的DTO模型;当然,你可以选择在反序列化之后将其解压缩回分层域模型
  • 选择一个模型(可能是您已有数据的模型),并继续在 实现中使用继承,并将实现建模为另一方的DTO

例如,如果你将java代码保持为多态,那么.NET代码需要具有魔术类型名称的上述内容,但是:这将使 真正 有些冲突是混乱的 - 例如,如果字段1在一个子类型中是int Foo,在另一个子类型中是string Bar:坏事;你还需要硬编码/识别pojo类型名称。在没有试图吹响我自己的小号的情况下,这些正是我在protobuf-net实现中努力规避的问题,冲突和名称依赖的类型

如果你保持protobuf-net是多态的,那么你可能只是从你发布的.proto开始,并且只是检查(在java端反序列化之后)ProgramInfoAndroid或ProgramInfoWindows是否为非null;然后基于哪个非null,将其映射到3种不同的域类型之一。