I have a proto:
message Foo {
oneof bar {
BarA bar_a = 1;
BarB bar_b = 2;
}
}
message BarA {
string text = 1;
}
message BarB {
int num = 1;
}
Then the code:
foo = &Foo{
Bar: &Foo_BarA{
BarA: &BarA{
Text: "text",
},
},
}
md := foo.ProtoReflect()
md.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
}
Inside md.Range, how do I get the field value from the fd, v pair? For example when it iterates over the Bar field, I want to get a BarA object and retrieve text. I need to use Range because I'm also using FieldOptions.
Update with more info
My first proto is that I'm actually working with (the above protos are contrived examples) is:
extend google.protobuf.FieldOptions {
string field_name = 50000;
}
extend google.protobuf.MessageOptions {
string template_name = 50000;
Type type = 50001;
}
message SendNotificationRequest {
Type type = 1;
Template template = 2;
}
enum Type {
TYPE_UNSPECIFIED = 0;
TYPE_EMAIL = 1;
TYPE_PUSH = 2;
}
message Template {
oneof template {
EmailTemplate email_template = 1;
PushTemplate push_template = 2;
}
}
message EmailTemplate {
option (type) = TYPE_EMAIL;
oneof template {
WelcomeEmailTemplate welcome_email_template = 1;
}
}
message WelcomeEmailTemplate {
option (template_name) = "welcome_email";
Field title = 1 [(field_name) = "main_title"];
Field user_name = 2 [(field_name) = "name"];
}
message Field {
oneof value {
string str = 1;
bool bool = 2;
int64 num = 3;
}
}
My second proto, part of another service+project, is:
message CreateNotificationRequest {
Type type = 1;
string template_name = 2;
map<string, Field> template_fields = 2;
}
enum Type {
TYPE_UNSPECIFIED = 0;
TYPE_EMAIL = 1;
TYPE_PUSH = 2;
}
message Field {
oneof value {
string str = 1;
bool bool = 2;
int64 num = 3;
}
}
I want to write one function that can convert any SendNotificationRequest to a CreateNotificationRequest. Let's call SendNotificationRequest's import alias api and CreateNotificationRequest's api2. I cannot change api2.
Not too relevant but I made the non-required decision to use typed Template for my service (api) instead of the generic map of api2, as I felt typed classes are more maintainable and less error-prone.
For an example conversion, this
&api.SendNotificationRequest{
Type: api.Type_TYPE_EMAIL,
Template: &api.Template {
Template: &api.Template_EmailTemplate{
EmailTemplate: &api.EmailTemplate{
Template: &api.EmailTemplate_WelcomeEmailTemplate{
Title: &api.Field{Value: &api.Placeholder_Str{Text: "Welcome Bob"}},
UserName: &api.Field{Value: &api.Placeholder_Str{Text: "Bob Jones"}},
},
},
},
},
}
should convert to:
&api2.CreateNotificationRequest{
Type: api2.Type_TYPE_EMAIL,
TemplateName: "welcome_email",
Template: map[string]*api2.Field{
"main_title": &api2.Field{Value: &api.Placeholder_Str{Text: "Welcome Bob"}},
"name": &api2.Field{Value: &api.Placeholder_Str{Text: "Bob Jones"}},
},
}
There may be new oneofs of EmailTemplate, and templates like WelcomeEmailTemplate may change its fields. But I want to write one function that doesn't need to be updated for those changes.
I believe this is very possible from the answer here.
I can nest md.Range on a SendNotificationRequest and its fields to process all its fields, and write a switch case to convert any Field oneof. I can also fetch FieldOption and MessageOption. While this happens, I build the CreateNotificationRequest.