【问题标题】:How to get protobuf-net and protostuff to mutually support inherited classes in .Net and Java?如何让 protobuf-net 和 protostuff 相互支持 .Net 和 Java 中的继承类?
【发布时间】:2013-09-18 12:06:49
【问题描述】:

我正在 Windows 系统上的基于 .Net 的程序和 Android 设备之间进行通信。在 .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?

【问题讨论】:

    标签: c# java protobuf-net protostuff


    【解决方案1】:

    首先,注意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 代码将需要类似于上面的带有魔法类型名称的内容,但是:这将得到 really 混乱的存在冲突 - 例如,如果字段 1 在一个子类型中是 int Foo,而在另一个子类型中是 string Bar:坏事;您还需要硬编码/识别 pojo 类型名称。不想吹我自己的小号,这些正是我在 protobuf-net 实现中努力规避的问题、冲突和名称依赖的类型

    如果您将 protobuf-net 保持为多态,那么您大概可以从您发布的 .proto 开始,然后检查(在 java 端反序列化之后)ProgramInfoAndroid 或 ProgramInfoWindows 是否为非空;然后根据哪个是非空的,将其映射到 3 种不同的域类型之一。

    【讨论】:

    • 感谢您的长篇回答(并感谢 protobuf-net)。如果没有其他人有建议或解决方案(提示、提示、轻推、轻推),我想我会在接下来的几个月里忍受当前的情况,同时获得更多的 Java 经验。然后我会回到这个并尝试自己做点什么。
    • @RenniePet 如果你真的想要,我可以尝试让它(protobuf-net)读写protostuff风格的继承。可以很有趣。但是,我 a: 认为 protobuf-net 的设计选择在这里更合适,并且 b: 向 protostuff 提交补丁的愿望完全为零。如果你想让我试试,如果你能提供一些示例模型和他们创建的 protobuf 二进制数据,那就太好了,即“这是一个带有 {这些值} 的 ProgramInfoBase,这是一个带有 {这些值} 的 ProgramInfoWindows,这是一个具有 {这些值} 的 ProgramInfoAndroid” - 你会为此而努力吗?
    • Marc,非常感谢您考虑将这种支持添加到 protobuf-net。我愿意在有限的范围内参与,因为我的参与可能会产生成效——但我真的宁愿等待几周左右,直到我更好地了解 protobuf-net - protostuff 交换的工作方式。我现在只是在玩它,没有真正的经验。此外,我想了解多级派生的含义,因为我想要来回移动的 C# 对象通常是基础对象的第 3 或第 4 级。再次感谢。
    • 您好,Marc,如约而至。但是现在我已经获得了更多使用 Java 处理 .Net 对象的经验,我认为我最初的愿望比它的价值更复杂。相反,我认为我更喜欢 Java 对象是派生的 .Net 对象的单个“扁平化”版本。所以我的建议是protobuf-net有一些属性可以放在.Net类定义上,说“在序列化过程中展平这个对象层次结构并在反序列化时去展平它”。 Serializer.GetProto() 方法创建了一个反映这一点的 .proto。
    • @RenniePet 它还会反序列化多个派生类型吗?还是只有一种?对于多个派生类型的场景,我怀疑在规范方面仍然存在很多漏洞......
    猜你喜欢
    • 2011-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多