Protobuf 3.0任何类型包装/拆包

Ove*_*olt 16 alpha unpack protocol-buffers pack any

我想知道如何将Protobuf Any Type转换为原始的Protobuf消息类型,反之亦然.从Message到Any的Java很容易:

Any.Builder anyBuilder = Any.newBuilder().mergeFrom(protoMess.build());
Run Code Online (Sandbox Code Playgroud)

但是我怎样才能解析出任何回到原始信息(例如"protoMess"的类型)?我可以解析一下流上的所有内容,只是为了重读它,但那不是我想要的.我希望有这样的转变:

ProtoMess.MessData.Builder protoMessBuilder = (ProtoMess.MessData.Builder) transformToMessageBuilder(anyBuilder)
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?它已经为Java实现了吗?该的Protobuf语言指南说,有包并解压方法,但有没有在Java中.先感谢您 :)

sun*_*nce 17

答案可能有点晚,但也许这对某人有帮助.

在当前版本的Protocol Buffers 3中pack,unpack可以使用Java.

在您的示例中,包装可以像:

Any anyMessage = Any.pack(protoMess.build()));
Run Code Online (Sandbox Code Playgroud)

并解压缩如下:

ProtoMess protoMess = anyMessage.unpack(ProtoMess.class);
Run Code Online (Sandbox Code Playgroud)

以下是使用嵌套Any消息处理Protocol Buffers消息的完整示例:

ProtocolBuffers文件

带有嵌套Any消息的简单Protocol Buffers文件可能如下所示:

syntax = "proto3";

import "google/protobuf/any.proto";

message ParentMessage {
  string text = 1;
  google.protobuf.Any childMessage = 2;
}
Run Code Online (Sandbox Code Playgroud)

可能的嵌套消息可能是:

syntax = "proto3";

message ChildMessage {
  string text = 1;
}
Run Code Online (Sandbox Code Playgroud)

填料

要构建完整消息,可以使用以下函数:

public ParentMessage createMessage() {
    // Create child message
    ChildMessage.Builder childMessageBuilder = ChildMessage.newBuilder();
    childMessageBuilder.setText("Child Text");
    // Create parent message
    ParentMessage.Builder parentMessageBuilder = ParentMessage.newBuilder();
    parentMessageBuilder.setText("Parent Text");
    parentMessageBuilder.setChildMessage(Any.pack(childMessageBuilder.build()));
    // Return message
    return parentMessageBuilder.build();
}
Run Code Online (Sandbox Code Playgroud)

开箱

要从父消息中读取子消息,可以使用以下函数:

public ChildMessage readChildMessage(ParentMessage parentMessage) {
    try {
        return parentMessage.getChildMessage().unpack(ChildMessage.class);
    } catch (InvalidProtocolBufferException e) {
        e.printStackTrace();
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:

如果打包的消息可以具有不同的类型,则可以读出typeUrl并使用反射来解压缩消息.假设您有子消息ChildMessage1,ChildMessage2您可以执行以下操作:

@SuppressWarnings("unchecked")
public Message readChildMessage(ParentMessage parentMessage) {
    try {
        Any childMessage = parentMessage.getChildMessage();
        String clazzName = childMessage.getTypeUrl().split("/")[1];
        String clazzPackage = String.format("package.%s", clazzName);
        Class<Message> clazz = (Class<Message>) Class.forName(clazzPackage);
        return childMessage.unpack(clazz);
    } catch (ClassNotFoundException | InvalidProtocolBufferException e) {
        e.printStackTrace();
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

为了进一步处理,您可以确定消息的类型instanceof,这不是非常有效.如果您想获得某种类型的消息,您应该typeUrl直接比较:

public ChildMessage1 readChildMessage(ParentMessage parentMessage) {
    try {
        Any childMessage = parentMessage.getChildMessage();
        String clazzName = childMessage.getTypeUrl().split("/")[1];
        if (clazzName.equals("ChildMessage1")) {
            return childMessage.unpack("ChildMessage1.class");
        }
        return null
    } catch (InvalidProtocolBufferException e) {
        e.printStackTrace();
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 除了这个`readChildMessage`之外没有别的办法了吗?如果我可能会收到数十种不同的消息怎么办?只需添加新的try-catch块?甚至“开关箱”之类也是绝对不能接受的。 (2认同)