【发布时间】:2021-07-10 15:16:08
【问题描述】:
对于我的基于 Java 的应用程序,我需要动态地将 Protobuf 消息转换为 Avro 记录。消息和记录的架构在编译时未知,但在运行时已知。此流程的 Avro 方面已完成,但我在 Protobuf 方面苦苦挣扎。如何将 Protobuf 消息作为字节数组获取,然后使用 DescriptorProto 作为消息类型将其转换为可以逐字段读取的内容?我花了相当长的时间来熟悉 Protobuf API 并寻找相关示例,但我的用例似乎并不常见。
这是我目前能做的:
- 从磁盘加载一个 FileDescriptorSet,它描述了我将在网络上遇到的消息类型,并为特定的消息类型选择适当的 DescriptorProto
- 遍历 DescriptorProto 的字段,以便我原则上知道如何解释特定消息的数据
- 使用生成的类构造测试消息,生成代表我在网络上可以预期的字节数组
我还不能做的事情:
- 给定字节数组,并给定 DescriptorProto 中的一个字段,选择该字段的消息值
我尝试将字节数组合并到基于 DescriptorProto 的各种构建器中,但它们似乎没有给我我需要的东西。我也无法从这些构建器中复制原始字节数组。显然我需要克服一些概念上的差距。
编辑:这里有一些关于我正在尝试做的额外细节。为简洁起见,代码已被简化。
从磁盘加载一个或多个DescriptorProto:
InputStream is = new FileInputStream("/path/to/mydescriptors.pb");
FileDescriptorSet set = FileDescriptorSet.parseFrom(is);
FileDescriptorProto geoposition = set.getFile(0);
DescriptorProto PositionEvent = geoposition.getMessageType(0);
填充 protoc 生成的域类的对象,用于测试:
Meters horizAcc = Meters.newBuilder().setValue(4.9f).build();
PositionEvent pe = PositionEvent.newBuilder()
.setPoint(point)
.setTime(time)
.setHorizontalAccuracy(horizAcc)
...
.build();
将域对象转换为字节数组(模拟线路上的数据):
byte[] bytes = pe.toByteArray();
将字节数组转换回域对象:
PositionEvent pe2 = PositionEvent.newBuilder().mergeFrom(bytes).build();
上面的mergeFrom 让我可以将字节数组转换为在编译时间之前生成的域类的实例。但是,在实际应用中,这个生成的类在编译时是不可用的。我需要将消息解析为通用类的实例,这将使我能够访问消息中各个字段的值,以及消息值字段、它们的字段等。
我认为Descriptor 或DescriptorProto 是我需要使用的类,但以下似乎不起作用:
DescriptorProtos.DescriptorProto p = positionEvent.toBuilder().mergeFrom(bytes).build();
至少,我不知道如何检索这个DescriptorProto中“horizontal_accuracy”字段的值以确定该值为4.9。
我可以想象一个基于反射的解决方案,在该解决方案中,我提前了解所有消息类型,使用 protoc 生成所有域类,然后使用反射填充适当的任何给定消息的域对象,并逐步遍历其字段。但是,这感觉像是一种 hack,不适合有成百上千的消息类型频繁变化的场景(就像我公司的情况)。
【问题讨论】:
-
嗨(来自校友)。只是好奇,您是如何“将字节数组合并到各种构建器中”的?如果您可以编辑您的问题以显示一些代码。
-
谢谢你,slackwing。我添加了一些示例代码来说明我想要做什么。还更新了我的个人资料:-)
标签: protocol-buffers protobuf-java