Protobuf 3.0任何类型包装/拆包

时间:2015-09-16 16:09:17

标签: protocol-buffers alpha pack unpack any

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

Any.Builder anyBuilder = Any.newBuilder().mergeFrom(protoMess.build());

但是如何将 Any 解析回原始消息(例如“protoMess”的类型)?我可以解析一下流上的所有内容,只是为了重读它,但这不是我想要的。我希望有这样的转变:

ProtoMess.MessData.Builder protoMessBuilder = (ProtoMess.MessData.Builder) transformToMessageBuilder(anyBuilder)

我怎样才能实现这一目标?它已经为Java实现了吗? Protobuf Language Guide表示有pack和unpack方法,但Java中没有。 提前谢谢你:)

3 个答案:

答案 0 :(得分:14)

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

当前版本的协议缓冲区3 packunpackavailable in Java

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

Any anyMessage = Any.pack(protoMess.build()));

解压缩如:

ProtoMess protoMess = anyMessage.unpack(ProtoMess.class);

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

ProtocolBuffers Files

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

syntax = "proto3";

import "google/protobuf/any.proto";

message ParentMessage {
  string text = 1;
  google.protobuf.Any childMessage = 2;
}

可能的嵌套消息可能是:

syntax = "proto3";

message ChildMessage {
  string text = 1;
}

包装

要构建完整消息,可以使用以下功能:

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();
}

启封

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

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

修改

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

@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;
    }
}

为了进一步处理,您可以使用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;
    }
}

答案 1 :(得分:2)

如果有人遇到相同的问题,只需添加信息...当前要解压您需要做的事情(c#.netcore 3.1 Google.Protobuf 3.11.4)

Foo myobject = anyMessage.Unpack<Foo>();

答案 2 :(得分:0)

我知道这个问题很老,但是当我寻找答案时仍然出现。使用@sundance答案,我不得不在回答方式上有所不同。问题在于实际消息是实际类的子类。因此它需要一个$。

    for(Any x : in.getDetailsList()){
            try{
                String clazzName = x.getTypeUrl().split("/")[1];
                String[] split_name = clazzName.split("\\.");
                String nameClass = String.join(".", Arrays.copyOfRange(split_name, 0, split_name.length - 1)) + "$" + split_name[split_name.length-1];
                Class<Message> clazz = (Class<Message>) Class.forName(nameClass);

                System.out.println(x.unpack(clazz));

            } catch (Exception e){
                e.printStackTrace();
            }
        } 

这就是我的原始消息的定义


    syntax = "proto3";
    package cb_grpc.msg.Main;

    service QueryService {
        rpc anyService (AnyID) returns (QueryResponse) {}
    }

    enum Buckets {
        main = 0;
        txn = 1;
        hxn = 2;
       }

    message QueryResponse{
        string content = 1;
        string code = 2;
    }

    message AnyID {
        Buckets bucket = 1;
        string docID = 2;
        repeated google.protobuf.Any details = 3;
    }


    syntax = "proto3";
    package org.querc.cb_grpc.msg.database;

    option java_package = "org.querc.cb_grpc.msg";
    option java_outer_classname = "database";

    message TxnLog {
        string doc_id = 1;
        repeated string changes = 2;
    }