首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从FieldDescriptor中获取字段值以及如何在protoreflect.Range中获取字段值?

如何从FieldDescriptor中获取字段值以及如何在protoreflect.Range中获取字段值?
EN

Stack Overflow用户
提问于 2021-09-11 02:17:32
回答 1查看 2.1K关注 0票数 2

我有一条原语:

代码语言:javascript
复制
message Foo {
  oneof bar {
    BarA bar_a = 1;
    BarB bar_b = 2;
  }
}

message BarA {
  string text = 1;
}

message BarB {
  int num = 1;
}

然后,代码:

代码语言:javascript
复制
foo = &Foo{
    Bar: &Foo_BarA{
        BarA: &BarA{
            Text: "text",
        },
    },
}
md := foo.ProtoReflect()
md.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {

}

md.Range内部,如何从fd, v对中获取字段值?例如,当它在Bar字段上迭代时,我希望获得一个BarA对象并检索text。我需要使用Range,因为我也在使用FieldOptions

使用更多信息更新

我的第一个观点是,我实际上正在使用(上面的原型都是人为的例子)是:

代码语言:javascript
复制
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;
  }
}

我的第二个proto是另一个service+project的一部分,它是:

代码语言:javascript
复制
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;
  }
}

我想编写一个可以将任何SendNotificationRequest转换为CreateNotificationRequest的函数。让我们调用SendNotificationRequest的导入别名apiCreateNotificationRequestapi2,我不能更改api2

不太相关,但我做了一个非必需的决定,使用类型化的Template进行服务(api),而不是使用api2的泛型映射,因为我觉得类型化的类更容易维护,更容易出错。

对于示例转换,如下所示

代码语言:javascript
复制
&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"}},
                },
            },
        },
    },
}

应改为:

代码语言:javascript
复制
&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"}},
    },
}

可能会有新的EmailTemplate,像WelcomeEmailTemplate这样的模板可能会改变它的字段。但是,我想编写一个不需要为这些更改进行更新的函数。

我相信这是很有可能的答案,here

我可以在nest md.Range上对SendNotificationRequest及其字段进行所有字段的处理,并编写一个转换用例来转换任何Field的字段。我还可以获取FieldOption和MessageOption。当这种情况发生时,我构建了CreateNotificationRequest

EN

回答 1

Stack Overflow用户

发布于 2021-09-11 07:35:48

您可以使用以下方法获得BarA的值:

代码语言:javascript
复制
    md.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
        w := v.Message().Get(fd.Message().Fields().ByNumber(1))
        fmt.Println(w) // prints "text"
        return false
    })

这是立即工作的,因为您的proto消息只有一个填充字段(bar_a)。如果您有更多的填充字段,您应该添加更多的检查。检查当前的FieldDescriptor是否是您要寻找的。更健壮的实现可能如下所示:

代码语言:javascript
复制
    md.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
        if fd.Name() == "bar_a" {
            if field := fd.Message().Fields().ByName("text"); field != nil {
                w := v.Message().Get(field)
                fmt.Println(w) // text
                return false
            }
        }
        return true
    })

使用ByName而不是ByNumber是有争议的,因为字段名在序列化中并不起作用,但是它提供了更易读的代码。

作为另一种选择,您可以通过类型断言v中的接口直接获得字段的具体值。

代码语言:javascript
复制
md.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
        if barA, ok := v.Message().Interface().(*BarA); ok {
            fmt.Println(barA.Text)
            return false
        }
        return true
    })
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69139530

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档