How to build a DynamicMessage?

173 views
Skip to first unread message

Siddharth Jain

unread,
Mar 12, 2023, 12:40:05 PM3/12/23
to Protocol Buffers
I am building an application that will be given protobuf definition at runtime. E.g.:

syntax = "proto3";
package testing;
message MessageWithComments {
    // Leading field comment.
    string foo = 1;
}

I want to be able to create messages of the given protobuf definition at runtime in my application. This is what I am trying:

First, I run:

protoc --descriptor_set_out ...

Then, I create a DescriptorProto:

FileDescriptorSet descriptorSet = FileDescriptorSet.parseFrom(bytes);
List<FileDescriptorProto> descriptorProtos = descriptorSet.getFileList();
DescriptorProto descriptor = DescriptorProtos.FileDescriptorProto
.parseFrom(descriptorProtos.get(0).toByteArray())
.getMessageType(0);

Then, I am trying to build the message like this: this whole code is lengthy but what I am doing is:
1. Create a message builder from the DescriptorProto in above step
2. Then iterate over the fields and populate then with values
 3. If a field is of type MESSAGE then we have to recursively fill it (go back to Step 1)

i am copying the code below but it gives me this error:
Exception in thread "main" java.lang.IllegalArgumentException: Wrong object type used with protocol message reflection.
Field number: 2, field java type: MESSAGE, value type: java.lang.String

        at com.google.protobuf.FieldSet$Builder.verifyType(FieldSet.java:1214)
        at com.google.protobuf.FieldSet$Builder.setField(FieldSet.java:1076)
        at com.google.protobuf.DynamicMessage$Builder.setField(DynamicMessage.java:545)

i tried with different protos and its the same error everytime. Can anybody help me please?
DynamicMessage message = printAllFieldsDp(descriptor);

public static DynamicMessage printAllFieldsDp(DescriptorProto dp) {
Builder builder = DynamicMessage.newBuilder(dp);
for(Map.Entry<FieldDescriptor, Object> entry : builder.getAllFields().entrySet()) {
FieldDescriptor d = entry.getKey();
String name = d.getFullName();
System.out.println("printing fields of " + name);
Object o = entry.getValue();
if (o != null) {
Object v = printAllFields(o);
builder.setField(d, v);
}
}
return builder.build();
}

private static Object printAllFields(Object o) {
if (o instanceof FieldDescriptorProto) {
FieldDescriptorProto p = (FieldDescriptorProto) o;
System.out.printf("%s %s %s\n", p.getName(), p.getType(), p.getTypeName()); // Edw_Create_Datetime TYPE_MESSAGE .NBkgsMeasure.Timestamp
switch (p.getType().toString()) {
case "TYPE_STRING":
return "test";
case "TYPE_INT32":
return 10;
case "TYPE_INT64":
return -100;
case "TYPE_FLOAT":
return 3.14156;
case "TYPE_MESSAGE":
return printAllFieldsFdp(p);
default:
throw new RuntimeException("not implemented");
}
}
if (o instanceof FieldDescriptor) {
FieldDescriptor fd = (FieldDescriptor)o;
System.out.printf("%s %s\n", fd.getName(), fd.getType()); // Summary_Quote_Flg TYPE_STRING
switch (fd.getType().toString()) {
case "TYPE_STRING":
return "test";
case "TYPE_INT32":
return 10;
case "TYPE_INT64":
return -100;
case "TYPE_FLOAT":
return 3.14156;
default:
throw new RuntimeException("not implemented");
}
}
if (o instanceof DescriptorProto) {
return printAllFieldsDp((DescriptorProto)o);
}
if (o instanceof List) {
List l = (List)o;
int n = l.size();
List t = new ArrayList();
for (int i = 0; i < n; i++) {
Object e = l.get(i);
t.add(printAllFields(e));
}
return t;
}
return o;
}

public static DynamicMessage printAllFieldsFdp(FieldDescriptorProto dp) {
Builder builder = DynamicMessage.newBuilder(dp);
for(Map.Entry<FieldDescriptor, Object> entry : builder.getAllFields().entrySet()) {
FieldDescriptor d = entry.getKey();
String name = d.getFullName();
System.out.println("printing fields of " + name);
Object o = entry.getValue();
if (o != null) {
Object v = printAllFields(o);
builder.setField(d, v);
}
}
return builder.build();
}

Jerry Berg

unread,
Mar 16, 2023, 4:09:35 PM3/16/23
to Protocol Buffers
Hi Siddharth,

Is the the proto definition you've provided the one that you are trying to populate?

syntax = "proto3";
package testing;
message MessageWithComments {
    // Leading field comment.
    string foo = 1;
}

There is no field number 2 as in your error message.

Can you provide some way to look at the code you are attempting? It is hard to know what is wrong without seeing the actual set up code.

Thanks,
Jerry

Reply all
Reply to author
Forward
0 new messages