0
votes

I want to create .proto file through java code. I am able to do it with message having primitive type attributes as follow :

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

Now my proto structure is

message Person{

optional string FName=1;
optional string LName=2;
optional string Status=3;}

And I can achieve this by using above methods.

I want to achieve:

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

Queries:

  1. How to add Address under Person(can try method addRepeatedField but couldn't create FileDescriptor)
  2. How to add Address as repeated field(ArrayList) under Person

Please give me any hint if anybody has.

2

2 Answers

0
votes

To add a type under Person call addNestedType() on messageProtoBuilderA (the DescriptorProto.Builder for Person). The input for addNestedType() is a DescriptorProto for Address, which is built in the same way as the one for Person.

When adding the address field to the Person type, call setLabel(Label.LABEL_REPEATED) on the corresponding 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;
}
0
votes

I believe for protos all the fields have to be together. So it should be

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

and you can put this under your definition of Address so that code is already generated.