【问题标题】:How to resolve a custom option in a protocol buffer FileDescriptor如何解析协议缓冲区 FileDescriptor 中的自定义选项
【发布时间】:2017-03-07 15:29:20
【问题描述】:

我在 Java 中使用协议缓冲区 2.5。我有一个定义自定义选项的原型文件。另一个 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 正是Protocol Buffers 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 定义的自定义选项。

以下代码将所有三个原型的二进制编码版本写入/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)

s 字段的 FieldDescriptor 选项应该有一个用于该自定义选项的字段,但它有一个未知字段。未知字段上的字段编号和值是正确的。只是自定义选项没有得到解决。我该如何解决?

【问题讨论】:

  • 遇到同样的事情 - 你有没有想过@user100464

标签: java protocol-buffers


【解决方案1】:

您需要使用FileDescriptorProto.parseFrom(byte[] data, ExtensionRegistryLite extensionRegistry,并显式创建ExtentionRegistry。这是创建扩展注册表的一种方法:

ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
com.example.proto.Options.registerAllExtensions(extensionRegistry);

(其中com.example.proto.Options 是编译后的自定义选项类)

这显然只有在您可以访问客户端的自定义选项编译文件时才有效。不知道有没有办法在客户端序列化扩展和反序列化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-25
    • 2023-03-13
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多