【问题标题】:Protobuf 3.0 Any Type pack/unpackProtobuf 3.0 任何类型的打包/解包
【发布时间】:2015-12-13 08:08:30
【问题描述】:

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

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

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

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

我怎样才能做到这一点?它已经为 Java 实现了吗? Protobuf Language Guide 说有打包和解包方法,但在 Java 中没有。 提前谢谢你:)

【问题讨论】:

    标签: protocol-buffers pack unpack any


    【解决方案1】:

    只是为了添加信息以防有人遇到同样的问题......目前要解压你必须做的(c# .netcore 3.1 Google.Protobuf 3.11.4)

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

    【讨论】:

      【解决方案2】:

      我知道这个问题很老了,但在我寻找答案时仍然出现。使用@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;
          } 
      
      

      【讨论】:

        【解决方案3】:

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

        在当前版本的 Protocol Buffers 3 中,packunpackavailable in Java

        在您的示例中,包装可以这样完成:

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

        然后像这样解包:

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

        这里也是处理带有嵌套Any消息的Protocol Buffers消息的完整示例:

        ProtocolBuffers 文件

        带有嵌套Any 消息的简单协议缓冲区文件可能如下所示:

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

        【讨论】:

        • 除了这个readChildMessage没有别的办法了吗?如果我有几十个不同的消息可以进来怎么办?只需添加新的try-catch 块?连switch-case之类的都是绝对不能接受的。
        • 好问题,我忘了补充一点,您可以将打包消息的名称设为typeURL。这允许通过反射解包任何消息或直接决定如何处理消息。我在回答中添加了两个示例,希望对您有所帮助。
        • 太棒了!它帮助了我!
        • childMessage.getTypeUrl().split("/")[1];不会给出java包名称吗?它将提供 proto 包,因此无法正常工作。
        • @AdityaJoshee 是正确的。理想情况下,我想获得类的规范名称,但您会获得 proto 文件的包。这很烦人。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-01-04
        • 2018-05-27
        • 2023-02-07
        • 2021-12-07
        • 2015-09-20
        • 1970-01-01
        • 2017-07-12
        相关资源
        最近更新 更多