Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested Protocol Buffers with reflection

Say I have a Message in a .proto file with the following contents

Message Foo {
    Message Bar {
        optional int32 a = 1;
        optional int32 b = 2;
    }
    optional Bar bar = 1;
}

In Java, is there anyway to set the field a using only the string "bar.a"? Ideally I'd like to write a method like below:

public Foo.Builder apply(Foo.Builder builder, String fieldPath, Object value) {
    // fieldPath == "bar.a"
    // This doesn't work
    FieldDescriptor fd = builder.getDefaultInstanceForType().findFieldByName(fieldPath);
    builder = builder.setField(fd, value);
}

But when I do this, I get an IllegalArgumentException.

Does anyone know how to do this in a generic fashion?

I also need to go the other way as well

public Object getValue(Foo message, String fieldPath) {
    // This doesn't work
    FieldDescriptor fd = message.getDefaultInstanceForType().findFieldByName(fieldPath);
    return message.getField(fieldPath);
}

As a note, this works fine if the fieldPath does not contain a separator (".") and references a base Message, but not a nested Message.

like image 659
Jon Avatar asked Dec 17 '13 05:12

Jon


1 Answers

You need to split the field path on '.' and do a chain of lookups, e.g.

Message subMessage =
    (Message)message.getField(
        message.getDescriptorForType().findFieldByName("bar"));
return subMessage.getField(
    subMessage.getDescriptorForType().findFieldByName("a"));

Or to write:

FieldDescriptor desc = message.getDescriptorForType().findFieldByName("bar");
Message.Builder subBuilder = (Message.Builder)builder.getFieldBuilder(desc);
subBuilder.setField(
    subMessage.getDescriptorForType().findFieldByName("a"), value);
builder.setField(desc, subBuilder.build());

You could of course write a library that splits the string and does all the lookups in one call (and do proper error checking).

like image 148
Kenton Varda Avatar answered Nov 06 '22 04:11

Kenton Varda