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

时间:2014-09-03 13:14:51

标签: java protocol-buffers

我想通过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下添加地址(可以尝试方法 addRepeatedField 但无法创建FileDescriptor)
  2. 如何在Person
  3. 下添加Address作为重复字段(ArrayList)

    如果有人的话,请给我任何暗示。

2 个答案:

答案 0 :(得分:0)

Person addNestedType()messageProtoBuilderA调用DescriptorProto.Builder下添加类型。 addNestedType()的输入是Address的DescriptorProto,其构造方式与Person的方式相同。

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

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;
}

答案 1 :(得分:0)

我相信所有的领域必须在一起。所以它应该是

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

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