【问题标题】:Protocol Buffers: How to parse a .proto file in Java协议缓冲区:如何在 Java 中解析 .proto 文件
【发布时间】:2023-03-13 16:45:02
【问题描述】:

我正在尝试在 Java 中动态解析给定的 .proto 文件以解码 Protobuf 编码的二进制文件。

我有以下解析方法,其中“proto”字符串包含.proto文件的内容:

public static Descriptors.FileDescriptor parseProto (String proto) throws InvalidProtocolBufferException, Descriptors.DescriptorValidationException {
        DescriptorProtos.FileDescriptorProto descriptorProto = DescriptorProtos.FileDescriptorProto.parseFrom(proto.getBytes());
        return Descriptors.FileDescriptor.buildFrom(descriptorProto, null);
}

但是,在执行之前的方法时会抛出异常,并显示消息“协议消息标签的线路类型无效。”。我使用来自 Google 的示例 .proto 文件,所以我猜它是有效的:https://github.com/google/protobuf/blob/master/examples/addressbook.proto

这是堆栈跟踪:

15:43:24.707 [pool-1-thread-1] ERROR com.github.whiver.nifi.processor.ProtobufDecoderProcessor - ProtobufDecoderProcessor[id=42c8ab94-2d8a-491b-bd99-b4451d127ae0] Protocol message tag had invalid wire type.
com.google.protobuf.InvalidProtocolBufferException$InvalidWireTypeException: Protocol message tag had invalid wire type.
    at com.google.protobuf.InvalidProtocolBufferException.invalidWireType(InvalidProtocolBufferException.java:115)
    at com.google.protobuf.UnknownFieldSet$Builder.mergeFieldFrom(UnknownFieldSet.java:551)
    at com.google.protobuf.GeneratedMessageV3.parseUnknownField(GeneratedMessageV3.java:293)
    at com.google.protobuf.DescriptorProtos$FileDescriptorSet.<init>(DescriptorProtos.java:88)
    at com.google.protobuf.DescriptorProtos$FileDescriptorSet.<init>(DescriptorProtos.java:53)
    at com.google.protobuf.DescriptorProtos$FileDescriptorSet$1.parsePartialFrom(DescriptorProtos.java:773)
    at com.google.protobuf.DescriptorProtos$FileDescriptorSet$1.parsePartialFrom(DescriptorProtos.java:768)
    at com.google.protobuf.AbstractParser.parsePartialFrom(AbstractParser.java:163)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:197)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:209)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:214)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:49)
    at com.google.protobuf.DescriptorProtos$FileDescriptorSet.parseFrom(DescriptorProtos.java:260)
    at com.github.whiver.nifi.parser.SchemaParser.parseProto(SchemaParser.java:9)
    at com.github.whiver.nifi.processor.ProtobufDecoderProcessor.lambda$onTrigger$0(ProtobufDecoderProcessor.java:103)
    at org.apache.nifi.util.MockProcessSession.write(MockProcessSession.java:895)
    at org.apache.nifi.util.MockProcessSession.write(MockProcessSession.java:62)
    at com.github.whiver.nifi.processor.ProtobufDecoderProcessor.onTrigger(ProtobufDecoderProcessor.java:100)
    at org.apache.nifi.processor.AbstractProcessor.onTrigger(AbstractProcessor.java:27)
    at org.apache.nifi.util.StandardProcessorTestRunner$RunProcessor.call(StandardProcessorTestRunner.java:251)
    at org.apache.nifi.util.StandardProcessorTestRunner$RunProcessor.call(StandardProcessorTestRunner.java:245)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

有什么想法吗? 谢谢!

【问题讨论】:

    标签: java protocol-buffers protobuf-java


    【解决方案1】:

    您似乎正在尝试使用FileDescriptorSet.parseFrom 来填充FileDescriptorSet。这仅适用于您提供的字节 二进制 protobuf 内容 - 也就是说:已编译 模式。您可以使用带有--descriptor_set_out 选项的protoc 命令行工具获得已编译 架构。您现在实际传递的是构成文本模式的文本字节,这不是parseFrom 所期望的。

    如果没有已编译的架构,您将需要一个运行时 .proto 解析器。我不知道有一个用于 Java 的。 protobuf-net 包括一个 (protobuf-net.Reflection),但那是 C#/.NET。如果没有可用的运行时 .proto 解析器,您需要 shell 执行 protoc

    【讨论】:

    • 我明白了,这是有道理的。我将尝试找到一种方法来编译我的 proto 文件。谢谢你的回答!
    • @WSH 应该只是protoc --descriptor_set_out
    • 好的,感谢您的精确。但是,如果有人听说过 Java proto 编译器,我仍然很感兴趣 :)
    • @WSH 可能没有 Java 实现。协议编译器比您想象的要复杂,并且以多种语言维护编译器的多个实现没有多大意义。您应该尝试做的是安排使用 --descriptor_set_out offline 解析您的 .protos,然后根据需要发送已编译的描述符集,而不是尝试按需解析整个 .proto 文件.
    • 嘿,@kenton,很高兴看到你还在回答 protobuf 问题。是的,当我终于开始编写一个 100% 的 c# 实现并通过我能找到的每个 .proto 运行它时,我发现了其中的一些复杂性,包括大多数公共 Google API 表面:)
    【解决方案2】:

    借鉴其他答案,这是我正在开发的库中工作 Kotlin 代码的 sn-p。 https://github.com/asarkar/okgrpc

    private fun lookupProtos(
        protoPaths: List<String>,
        protoFile: String,
        tempDir: Path,
        resolved: MutableSet<String>
    ): List<DescriptorProtos.FileDescriptorProto> {
        val schema = generateSchema(protoPaths, protoFile, tempDir)
        return schema.fileList
            .filter { resolved.add(it.name) }
            .flatMap { fd ->
                fd.dependencyList
                    .filterNot(resolved::contains)
                    .flatMap { lookupProtos(protoPaths, it, tempDir, resolved) } + fd
            }
    }
    
    private fun generateSchema(
        protoPaths: List<String>,
        protoFile: String,
        tempDir: Path
    ): DescriptorProtos.FileDescriptorSet {
        val outFile = Files.createTempFile(tempDir, null, null)
        val stderr = ByteArrayOutputStream()
        val exitCode = Protoc.runProtoc(
            (protoPaths.map { "--proto_path=$it" } + listOf("--descriptor_set_out=$outFile", protoFile)).toTypedArray(),
            DevNull,
            stderr
        )
        if (exitCode != 0) {
            throw IllegalStateException("Failed to generate schema for: $protoFile")
        }
        return Files.newInputStream(outFile).use { DescriptorProtos.FileDescriptorSet.parseFrom(it) }
    }
    

    这个想法是使用os72/protoc-jar 写出一个编译的模式/文件描述符。然后使用FileDescriptorSet.parseFrom 读取该文件,并递归其依赖项。

    【讨论】:

      【解决方案3】:

      不要使用 java String 来保存 protobuf 有效负载。问题是String 在幕后进行翻译,并对字符集做出假设。

      Protobuf 适用于字节数组,并且数组中的精确表示必须保持不变。往返String 不起作用。

      【讨论】:

      • 这取决于他们是加载数据,还是加载schema。架构(.proto 格式)是文本。
      • 正如 Bob 所说,我正在尝试解析文本文件,所以我猜 String 应该不是问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-05
      • 2017-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多