我想知道如何将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中没有。 提前谢谢你:)
答案 0 :(得分:14)
答案可能有点晚,但也许这对某人有帮助。
当前版本的协议缓冲区3 pack
和unpack
为available in Java。
在您的示例中,打包可以像:
Any anyMessage = Any.pack(protoMess.build()));
解压缩如:
ProtoMess protoMess = anyMessage.unpack(ProtoMess.class);
以下是使用嵌套Any
消息处理Protocol Buffers消息的完整示例:
带有嵌套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
并使用反射来解压缩消息。假设您有子邮件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;
}
}
为了进一步处理,您可以使用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;
}