使用协议缓冲区进行多态性的正确方法是什么?

发布于 2024-09-05 00:33:31 字数 239 浏览 3 评论 0原文

我正在尝试长期序列化一堆与 java 中强大的类层次结构相关的对象,并且我想使用协议缓冲区来完成此操作,因为它们的简单性、性能和易于升级。然而,它们并没有为多态性提供太多支持。现在,我处理它的方式是通过一个“一条消息来统治所有”解决方案,该解决方案具有一个必需的字符串 uri 字段,允许我通过反射实例化正确的类型,然后是一堆可选字段我可以序列化其他可能的类,但只会使用其中之一(基于 uri 字段的值)。有没有更好的方法来处理多态性,或者这是否像我想要的那样好?

I'm trying to long-term serialize a bunch of objects related by a strong class hierarchy in java, and I'd like to use protocol buffers to do it due to their simplicity, performance, and ease of upgrade. However, they don't provide much support for polymorphism. Right now, the way I'm handling it is by having a "one message to rule them all" solution that has a required string uri field that allows me to instantiate the correct type via reflection, then a bunch of optional fields for all the other possible classes I could serialize, only one of which will be used (based on the value of the uri field). Is there a better way to handle polymorphism, or is this as good as I'm going to get?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

别闹i 2024-09-12 00:33:31

proto3 中,extend 关键字已被替换。
来自文档如果您已经熟悉 proto2 语法,Any 类型取代了扩展。

syntax = "proto3";

import "google/protobuf/any.proto";

message Foo {
  google.protobuf.Any bar = 1;
}

但要注意:Any 本质上是一个字节 blob。大多数时候,最好使用 Oneof

syntax = "proto3";

message A {
    string a = 1;
}

message B {
    string b = 1;
}

message Foo {
  oneof bar {
    A a = 1;
    B b = 2;
  }
}

In proto3 the extend keyword has been replaced.
From the docs: If you are already familiar with proto2 syntax, the Any type replaces extensions.

syntax = "proto3";

import "google/protobuf/any.proto";

message Foo {
  google.protobuf.Any bar = 1;
}

But beware: Any is essentially a bytes blob. Most of the times it is better to use Oneof:

syntax = "proto3";

message A {
    string a = 1;
}

message B {
    string b = 1;
}

message Foo {
  oneof bar {
    A a = 1;
    B b = 2;
  }
}
你在我安 2024-09-12 00:33:31

有几种实现多态性的技术。我尝试在这里涵盖所有内容: 协议缓冲区多态性

我首选的方法使用嵌套 扩展

message Animal
{
    extensions 100 to max;

    enum Type
    {
        Cat = 1;
        Dog = 2;
    }

    required Type type = 1;
}

message Cat
{
    extend Animal
    {
        required Cat animal = 100; // Unique Animal extension number
    }

    // These fields can use the full number range.
    optional bool declawed = 1;
}

message Dog
{
    extend Animal
    {
        required Dog animal = 101; // Unique Animal extension number
    }

    // These fields can use the full number range.
    optional uint32 bones_buried = 1;
}

There are a few techniques for implementing polymorphism. I try to cover them all here: Protocol Buffer Polymorphism

My preferred approach uses nested extensions:

message Animal
{
    extensions 100 to max;

    enum Type
    {
        Cat = 1;
        Dog = 2;
    }

    required Type type = 1;
}

message Cat
{
    extend Animal
    {
        required Cat animal = 100; // Unique Animal extension number
    }

    // These fields can use the full number range.
    optional bool declawed = 1;
}

message Dog
{
    extend Animal
    {
        required Dog animal = 101; // Unique Animal extension number
    }

    // These fields can use the full number range.
    optional uint32 bones_buried = 1;
}
诺曦 2024-09-12 00:33:31

乔恩的解决方案是正确的并且有效,但很奇怪(对我来说)。但 Protocol Buffers 非常简单,所以你可以这样做:

enum Type {
    FOO = 0;
    BAR = 1;
  }

message Foo {
  required Type type = 1;
}

message Bar {
  required Type type = 1;
  required string text = 2;
}

基本上消息 Bar 扩展了消息 Foo (当然从实用的角度来看)。 Java 中的实现也很简单:

Bar bar = Bar.newBuilder().setType(Type.BAR).setText("example").build();
byte[] data = bar.toByteArray();

----

Foo foo = Foo.parseFrom(data);
if(foo.getType() == Type.BAR){
   Bar bar = Bar.parseFrom(data);
   System.out.println(bar.getText());
}

我知道,这不是一个优雅的解决方案,但它简单且符合逻辑。

Jon's solution is correct and working but pretty weird (for me). But Protocol Buffers is quite simple, so You can do something like that:

enum Type {
    FOO = 0;
    BAR = 1;
  }

message Foo {
  required Type type = 1;
}

message Bar {
  required Type type = 1;
  required string text = 2;
}

Basically message Bar extends message Foo (from practical side of course). Implementation in Java is simple too:

Bar bar = Bar.newBuilder().setType(Type.BAR).setText("example").build();
byte[] data = bar.toByteArray();

----

Foo foo = Foo.parseFrom(data);
if(foo.getType() == Type.BAR){
   Bar bar = Bar.parseFrom(data);
   System.out.println(bar.getText());
}

I known, it's not an elegant solution, but it's simple and logical.

素年丶 2024-09-12 00:33:31

请查看扩展程序和嵌套扩展程序,以一种更简洁的方式做这个。

Check out Extensions and Nested Extensions for a slightly cleaner way to do this.

海夕 2024-09-12 00:33:31

您是否考虑过使用扩展程序?您可以让 uri 字段确定要使用的类型,然后加载适当的扩展。如果您知道您的字段是互斥的,那么您可以在不同的扩展之间重用字段 ID。

您必须自己处理这一切,因为协议缓冲区的设计目的不是超越简单的值列表进行自我描述。这在谷歌技术页面中有所涉及。

Have you considered using extensions? You could have your uri field determine the type to use and then just load the appropriate extensions. If you know your fields are mutually exclusive then you could reuse the field id between separate extensions.

You have to handle this all yourself because protocol buffers aren't designed to be self describing beyond a simple list of values. This is touched on in the google techniques page.

辞慾 2024-09-12 00:33:31

对我来说,这是一个比 @Łukasz Marciniak 的答案更好的解决方案。

如果 Bar 扩展了 Foo,只需编写:

message Bar {
   optional Foo foo = 1;
   optional double aDouble = 2;
}
message Foo {
   optional string aString = 1;
}

因此,如果 Foo 演化,则仅修改 Foo 消息。

A solution a little better, for me, that the @Łukasz Marciniak's answer.

If Bar extends Foo, simply write:

message Bar {
   optional Foo foo = 1;
   optional double aDouble = 2;
}
message Foo {
   optional string aString = 1;
}

So if Foo evolves only Foo message is modified.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文