我正在使用Java协议缓冲区2.5。我有一个定义自定义选项的proto文件。另一个proto文件使用该自定义选项。如果我保留相应的FileDescriptorProto
然后读取它们并将它们转换为FileDescriptors
,则对自定义选项的引用将显示为未知字段。如何正确解析自定义选项?
这是代码。我有两个.proto文件。 protobuf-options.proto
看起来像这样:
package options;
import "google/protobuf/descriptor.proto";
option java_package = "com.example.proto";
option java_outer_classname = "Options";
extend google.protobuf.FieldOptions {
optional bool scrub = 50000;
}
导入的google/protobuf/descriptor.proto
正是协议缓冲区2.5附带的descriptor.proto
。
example.proto
看起来像这样:
package example;
option java_package = "com.example.protos";
option java_outer_classname = "ExampleProtos";
option optimize_for = SPEED;
option java_generic_services = false;
import "protobuf-options.proto";
message M {
optional int32 field1 = 1;
optional string field2 = 2 [(options.scrub) = true];
}
如您所见,field2
引用了protobuf-options.proto
定义的自定义选项。
以下代码将所有三个protos的二进制编码版本写入/tmp
:
package com.example;
import com.google.protobuf.ByteString;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.example.protos.ExampleProtos;
import java.io.FileOutputStream;
import java.io.OutputStream;
/**
*
*/
public class PersistFDs {
public void persist(final FileDescriptor fileDescriptor) throws Exception {
System.out.println("persisting "+fileDescriptor.getName());
try (final OutputStream outputStream = new FileOutputStream("/tmp/"+fileDescriptor.getName())) {
final FileDescriptorProto fileDescriptorProto = fileDescriptor.toProto();
final ByteString byteString = fileDescriptorProto.toByteString();
byteString.writeTo(outputStream);
}
for (final FileDescriptor dependency : fileDescriptor.getDependencies()) {
persist(dependency);
}
}
public static void main(String[] args) throws Exception {
final PersistFDs self = new PersistFDs();
self.persist(ExampleProtos.getDescriptor());
}
}
最后,以下代码从/tmp
加载那些原型,将它们转换回FileDescriptors
,然后检查field2
上的自定义选项:
package com.example;
import com.google.protobuf.ByteString;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.UnknownFieldSet.Field;
import java.io.FileInputStream;
import java.io.InputStream;
/**
*
*/
public class LoadFDs {
public FileDescriptorProto loadProto(final String filePath) throws Exception {
try (final InputStream inputStream = new FileInputStream(filePath)) {
final ByteString byteString = ByteString.readFrom(inputStream);
final FileDescriptorProto result = FileDescriptorProto.parseFrom(byteString);
return result;
}
}
public static void main(final String[] args) throws Exception {
final LoadFDs self = new LoadFDs();
final FileDescriptorProto descriptorFDProto = self.loadProto("/tmp/google/protobuf/descriptor.proto");
final FileDescriptorProto optionsFDProto = self.loadProto("/tmp/protobuf-options.proto");
final FileDescriptorProto fakeBoxcarFDProto = self.loadProto("/tmp/example.proto");
final FileDescriptor fD = FileDescriptor.buildFrom(descriptorFDProto, new FileDescriptor[0]);
final FileDescriptor optionsFD = FileDescriptor.buildFrom(optionsFDProto, new FileDescriptor[] { fD });
final FileDescriptor fakeBoxcarFD = FileDescriptor.buildFrom(fakeBoxcarFDProto, new FileDescriptor[] { optionsFD });
final FieldDescriptor optionsFieldDescriptor = optionsFD.findExtensionByName("scrub");
if (optionsFieldDescriptor == null) {
System.out.println("Did not find scrub's FieldDescriptor");
System.exit(1);
}
final FieldDescriptor sFieldDescriptor = fakeBoxcarFD.findMessageTypeByName("M").findFieldByName("field2");
System.out.println("unknown option fields "+sFieldDescriptor.getOptions().getUnknownFields());
final boolean hasScrubOption = sFieldDescriptor.getOptions().hasField(optionsFieldDescriptor);
System.out.println("hasScrubOption: "+hasScrubOption);
}
}
当我运行LoadFDs
时,它会因此异常而失败:
未知选项字段50000:1
线程“main”中的异常java.lang.IllegalArgumentException:FieldDescriptor与消息类型不匹配。 在com.google.protobuf.GeneratedMessage $ ExtendableMessage.verifyContainingType(GeneratedMessage.java:812) 在com.google.protobuf.GeneratedMessage $ ExtendableMessage.hasField(GeneratedMessage.java:761) 在com.example.LoadFDs.main(LoadFDs.java:42)
FieldDescriptor
字段的s
选项应该包含该自定义选项的字段,但它具有未知字段。未知字段的字段编号和值是正确的。只是自定义选项没有得到解决。我该如何解决这个问题?
答案 0 :(得分:0)
您需要使用 FileDescriptorProto.parseFrom(byte[] data, ExtensionRegistryLite extensionRegistry,并明确创建一个 ExtentionRegistry
。这是创建扩展注册表的一种方法:
ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
com.example.proto.Options.registerAllExtensions(extensionRegistry);
(其中 com.example.proto.Options
是编译的自定义选项类)
这显然只有在您有权访问客户端的自定义选项编译文件时才有效。不知道有没有办法在客户端序列化扩展和反序列化。