【问题标题】:Generic Parsing of PB in javajava中PB的泛型解析
【发布时间】:2010-10-27 15:40:18
【问题描述】:

是否可以在 Java 中以通用方式解析 protobuf?

我查看了 GeneratedMessage,但找不到将任何 PB 字节缓冲区解析为 GeneratedMessage 的方法。

本质上,我正在尝试将 PB 字节缓冲区解析为 GeneratedMessage,然后我会使用反射来检测其中的字段。

【问题讨论】:

    标签: java protocol-buffers


    【解决方案1】:

    首先,不知道架构是无法解析 PB 数据的。架构最初来自“.proto”文件,通常嵌入在protoc 生成的代码中。但是,您也可以告诉 protoc 以 Java Protobuf 库可用的格式存储架构:

    protoc --descriptor_set_out=mymessages.desc mymessages.proto
    

    然后将其加载到您的 Java 代码中:

    FileInputStream fin = new FileInputStream("mymessages.desc");
    Descriptors.FileDescriptorSet set =
      Descriptors.FileDescriptorSet.parseFrom(fin);
    Descriptors.Descriptor md = set.getFile(0).getMessageType(0);
    

    一旦您有了消息的架构 (Descriptor.Descriptor),解析消息就很容易了:

    byte[] data = ...;
    DynamicMessage m = DynamicMessage.parseFrom(md, data);
    

    DynamicMessage 有一个反射 API,可让您查看字段。

    混乱的部分是调用protoc 工具将“.proto”文件转换为可用格式。 C++ Protobuf 库有办法直接加载“.proto”文件,但不幸的是 Java Protobuf 库没有。

    【讨论】:

      【解决方案2】:

      您可以使用 UnknownFieldSet 解析通用 protobuf 消息。

      然后您可以使用提供的方法获取各个字段(例如asMap()hasField()getField()

      例如(数据取自this question):

          byte[] msg = new byte[] { 0x0a, 0x10, 0x08, 0x7f, (byte)0x8a, 0x01, 0x04, 0x08, 0x02, 0x10, 0x03, (byte)0x92, 0x01, 0x04, 0x08, 0x02, 0x10, 0x03, 0x18, 0x01};
          UnknownFieldSet eee = UnknownFieldSet.parseFrom(msg);
          System.out.println(eee.toString());
      

      给予:

      1: {
        1: 127
        17: {
          1: 2
          2: 3
        }
        18: {
          1: 2
          2: 3
        }
      }
      3: 1
      

      【讨论】:

        【解决方案3】:

        这是正确的例子:

        private static DynamicMessage parseData(byte[] data) throws IOException, DescriptorValidationException {
            FileInputStream fin = new FileInputStream("test.desc");
            DescriptorProtos.FileDescriptorSet set = DescriptorProtos.FileDescriptorSet.parseFrom(fin);
            Descriptor md = Descriptors.FileDescriptor.buildFrom(set.getFile(0), new  Descriptors.FileDescriptor[] {}).findMessageTypeByName("Person");
            return DynamicMessage.parseFrom(md, data);
        }
        

        【讨论】:

        • 如果你需要使用 findMessageTypeByName("Person") 是不是意味着你需要在解析之前知道对象是什么类型?
        【解决方案4】:

        我已经使用最后一个 protobuf v.3.1.0 测试了有效的解决方案

        这是根据以前的答案提出的升级解决方案。感谢两位作者。

        import com.example.address.AddressBookManager;
        import com.example.address.AddressBookProtos.AddressBook;
        import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
        import com.google.protobuf.Descriptors;
        import com.google.protobuf.Descriptors.Descriptor;
        import com.google.protobuf.Descriptors.FileDescriptor;
        import com.google.protobuf.DynamicMessage;
        
        import java.io.File;
        import java.io.InputStream;
        
        public class DynamicMessageDemo {
        
            private static final String ADDRESS_BOOK_SOURCE_FILENAME = "test.ab";
            private static final String ADDRESS_BOOK_DESC_FILENAME =
                File.separator + "address.desc";
        
            public static void main(String[] args) throws Exception {
        
                InputStream is = DynamicMessageDemo.class
                        .getResourceAsStream(ADDRESS_BOOK_DESC_FILENAME);
        
                FileDescriptorSet set = FileDescriptorSet.parseFrom(is);
                FileDescriptor fd = Descriptors.FileDescriptor.buildFrom(
                        set.getFile(0), 
                        new Descriptors.FileDescriptor[]{}
                        );
        
                // "AddressBook" is the second message in my *.proto
                // so index must be '1'
                Descriptor messageType = fd.getMessageTypes().get(1);
        
                // for testing purpose 
                AddressBook book = AddressBookManager.readFromFile(ADDRESS_BOOK_SOURCE_FILENAME);
                byte[] data = book.toByteArray();
        
                DynamicMessage message = DynamicMessage.parseFrom(messageType, data);
        
                System.out.println("\n Dynamic message:\n" + message);
            }
        }
        

        【讨论】:

          【解决方案5】:

          这是另一种通用解析 .desc 文件的方法:

          FileInputStream fin = new FileInputStream(descPath);
          DescriptorProtos.FileDescriptorSet set = DescriptorProtos.FileDescriptorSet.parseFrom(fin);
          
          List<FileDescriptor> dependencyFileDescriptorList = new ArrayList<>();
          for(int i=0; i<set.getFileCount()-1;i++) {
              dependencyFileDescriptorList.add(Descriptors.FileDescriptor.buildFrom(set.getFile(i), new  Descriptors.FileDescriptor[] {}));
          }
          FileDescriptor desc = Descriptors.FileDescriptor.buildFrom(set.getFile(set.getFileCount()-1), dependencyFileDescriptorList.toArray(new FileDescriptor[0]));
          

          【讨论】:

          • 看起来很有趣,但是太短了没有解释,我不明白为什么buildFrom两次。你在构建一个 desc 链吗?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-07-14
          • 1970-01-01
          • 1970-01-01
          • 2012-04-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多