如何在proto(Google Protocol Buffer)中编程添加用户定义的类型属性



我想通过java代码创建.proto文件。我可以用具有如下原始类型属性的消息来完成:

public void testDynamicProto() throws Exception {
    byte[] bytes = buildPersonProtoDesc();
    byte[] personBytes = buildPersonProto(bytes);
    Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor
            .buildFrom(
                    DescriptorProtos.FileDescriptorProto.parseFrom(bytes),
                    new Descriptors.FileDescriptor[0]);
    Descriptors.Descriptor personDesc = fileDescriptor
            .findMessageTypeByName(PERSON_MESSAGE);
    DynamicMessage message = DynamicMessage.parseFrom(personDesc,
            personBytes);
    for (Map.Entry<Descriptors.FieldDescriptor, Object> entry : message
            .getAllFields().entrySet()) {
        // TODO: add asserts
        System.out.println(entry.getKey().getName() + "------------"
                + entry.getValue());
    }
    // TODO: test repeated field
    // TODO: test non destructive updates (addition of column) to person proto and make sure old protos can be parsed
}

private byte[] buildPersonProto(byte[] bytes)
        throws Descriptors.DescriptorValidationException,
        InvalidProtocolBufferException {
    Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor
            .buildFrom(
                    DescriptorProtos.FileDescriptorProto.parseFrom(bytes),
                    new Descriptors.FileDescriptor[0]);
    Descriptors.Descriptor personDesc = fileDescriptor
            .findMessageTypeByName(PERSON_MESSAGE);
    DynamicMessage.Builder personBuilder = DynamicMessage
            .newBuilder(personDesc);
    personBuilder.setField(personDesc.findFieldByName(FNAME_FIELD), "Jon");
    personBuilder.setField(personDesc.findFieldByName(LNAME_FIELD), "Doe");
    personBuilder.setField(personDesc.findFieldByName(STATUS_FIELD), 2);

    return personBuilder.build().toByteArray();
}
private byte[] buildPersonProtoDesc() {
    DescriptorProtos.FileDescriptorProto.Builder fileDescriptorProtoBuilder = DescriptorProtos.FileDescriptorProto
            .newBuilder();
    DescriptorProtos.DescriptorProto.Builder messageProtoBuilderA = DescriptorProtos.DescriptorProto
            .newBuilder();
    messageProtoBuilderA.setName(PERSON_MESSAGE);
    messageProtoBuilderA
            .addFieldBuilder()
            .setName(FNAME_FIELD)
            .setNumber(1)
            .setType(DescriptorProtos.FieldDescriptorProto.Type.TYPE_STRING);
    messageProtoBuilderA
            .addFieldBuilder()
            .setName(LNAME_FIELD)
            .setNumber(2)
            .setType(DescriptorProtos.FieldDescriptorProto.Type.TYPE_STRING);
    messageProtoBuilderA.addFieldBuilder().setName(STATUS_FIELD)
            .setNumber(3)
            .setType(DescriptorProtos.FieldDescriptorProto.Type.TYPE_INT32);

    fileDescriptorProtoBuilder.addMessageType(messageProtoBuilderA);
    DescriptorProtos.FileDescriptorProto fileDescriptorProto = fileDescriptorProtoBuilder
            .build();
    return fileDescriptorProto.toByteArray();
}

现在我的原型结构是

message Person{
optional string FName=1;
optional string LName=2;
optional string Status=3;}

我可以通过使用上述方法来实现这一点。

我想实现:

message Person{
optional string FName=1;
optional string LName=2;
optional string Status=3;
message Address {
    optional string country=1;
    optional string state=2;
    optional string city=3;}
repeated Address address=4;}

查询:

  1. 如何在Person下添加Address(可以尝试方法addRepeatedField,但无法创建FileDescriptor)
  2. 如何在"人员"下添加"地址"作为重复字段(ArrayList)

如果有人有,请给我任何提示。

要在Person下添加类型,请调用messageProtoBuilderA上的addNestedType()(表示Person的DescriptorProto.Builder)。addNestedType()的输入是Address的DescriptorProto,其构建方式与Person相同。

address字段添加到Person类型时,在相应的FieldDescriptorProto.Builder上调用setLabel(Label.LABEL_REPEATED)

DescriptorProto.Builder messageProtoBuilderA = ...;
messageProtoBuilderA.addNestedType(createAddressType());
FieldDescriptorProto.Builder addressField = FieldDescriptorProto.newBuilder();
addressField.setName("address")
            .setLabel(Label.LABEL_REPEATED)
            .setNumber(4)
            .setType(Type.TYPE_MESSAGE)
            .setTypeName("Address");
messageProtoBuilderA.addField(addressField);
...
private DescriptorProto.Builder createAddressType() {
    DescriptorProto.Builder addressProtoBuilder = DescriptorProto.newBuilder();
    addressProtoBuilder.setName("Address");
    // add fields
    return addressProtoBuilder;
}

我相信对于protos,所有字段都必须在一起。所以应该是

optional string FName=1;
optional string LName=2;
optional string Status=3;
repeated Address address=4;

你可以把它放在你的地址定义下,这样代码就已经生成了。

最新更新