获取一个FieldDescriptor而不硬编码字段名(proto消息)

huangapple go评论121阅读模式
英文:

Get a FieldDescriptor without hard-coding the field name (proto message)

问题

给定以下的 proto message:

  1. message MyMsg {
  2. string my_field = 1;
  3. string your_field=2;
  4. }

...可以使用 protoreflect 包来获取每个字段的描述符:

  1. protoMessage := myMsg.ProtoReflect()
  2. messageDescriptor := protoMessage.Descriptor() // protobuf 类型信息
  3. fieldDescriptors := messageDescriptor.Fields() // 字段声明列表

获取特定字段的 field descriptor 很简单:

  1. fieldDescriptor := fieldDescriptors.ByTextName("my_field") // 描述一个字段

是否可以在不硬编码字段名 "my_field" 的情况下实现这一点?我猜如果能使用生成的代码来引用我感兴趣的字段会很好。类似于(不是有效的代码):

  1. fieldDescriptor := fieldDescriptors.ByTextName(pb.MyMsg.MyField) // 描述一个字段

这样,如果字段名发生变化,它将在编译时被捕获,甚至可以通过 IDE 的静态分析来捕获。

FieldDescriptors 类型有三个通过名称获取字段描述符的方法:

  • ByName(s Name)
  • ByJSONName(s string)
  • ByTextName(s string)

ByJSONNameByTextName 都需要硬编码的字段名(作为字符串),而 ByName 接受一个 Name,它被类型化为字符串。总之,我在 protoreflect 包中没有找到指向解决方案的任何内容。

背景

Field masks 是支持部分资源更新的推荐方法。在字段掩码中提供的掩码上进行迭代非常简单:

  1. protoMessage := myMsg.ProtoReflect()
  2. messageDescriptor := protoMessage.Descriptor() // protobuf 类型信息
  3. fieldDescriptors := messageDescriptor.Fields() // 字段声明列表
  4. // 在字段掩码中迭代字段路径
  5. for _, p := range mask.GetPaths() {
  6. // 找到字段路径的字段描述符
  7. fieldDescriptor := fieldDescriptors.ByTextName(p)
  8. if fieldDescriptor == nil {
  9. // 找不到字段路径的字段描述符
  10. return
  11. }
  12. // 太好了,字段路径指向一个字段,让我们使用它
  13. switch p {
  14. case "my_field":
  15. // 客户端想要更新 MyMsg.my_field
  16. case "your_field":
  17. // 客户端想要更新 MyMsg.your_field
  18. }
  19. }

问题是,为了实际更新 MyMsg 中的正确字段,有必要在 switch 语句中硬编码字段名。

英文:

Given the following proto message

  1. message MyMsg {
  2. string my_field = 1;
  3. string your_field=2;
  4. }

...the protoreflect package can be used to get a descriptor for each field

  1. protoMessage := myMsg.ProtoReflect()
  2. messageDescriptor := protoMessage.Descriptor() // protobuf type information
  3. fieldDescriptors := messageDescriptor.Fields() // list of field declarations

Getting a field descriptor for a specific field is trivial

  1. fieldDescriptor := fieldDescriptors.ByTextName("my_field") // describes a field

Can this be achieved without hard-coding the field name "my_field"? I guess it would be nice to use the generated code to refer to the field I'm interested in. Something like (not working code)

  1. fieldDescriptor := fieldDescriptors.ByTextName(pb.MyMsg.MyField) // describes a field

This way, if the field name changes, it will be caught at compile time, or even from static analysis by an IDE.

The FieldDescriptors type has three methods for getting a field descriptor by name:

  • ByName(s Name)
  • ByJSONName(s string)
  • ByTextName(s string)

ByJSONName and ByTextName both require hard-coded field names (as strings), and by ByName accepts a Name which is type'd to a string. The upshot is, I don't see anything in the protoreflect package that points to a solution.

Context

Field masks are the recommended way to support partial resource updates. It's trivial to iterate over the masks provided in a field mask

  1. protoMessage := myMsg.ProtoReflect()
  2. messageDescriptor := protoMessage.Descriptor() // protobuf type information
  3. fieldDescriptors := messageDescriptor.Fields() // list of field declarations
  4. // iterate over the field paths in the field mask
  5. for _, p := range mask.GetPaths() {
  6. // find the field descriptor for the field path
  7. fieldDescriptor := fieldDescriptors.ByTextName(p)
  8. if fieldDescriptor == nil {
  9. // field descriptor cannot be found for the field path
  10. return
  11. }
  12. // great, the field path points to a field, let's use it
  13. switch p {
  14. case "my_field":
  15. // the client wants to update MyMsg.my_field
  16. case "your_field":
  17. // the client wants to update MyMsg.your_field
  18. }
  19. }

The problem is, in order to actually update the correct field in MyMsg, it's necessary to hard-code the field name in the switch statement.

答案1

得分: 1

接口也有ByNumber方法。

我假设(我没有使用过这个方法),你可以从你的示例中给出1

当然还有Get方法,这样你就可以枚举所有的FieldDescriptors。

我认为想要反射生成的(结构体)类型并在它们之间进行枚举是合理的,但感觉像是无穷递归。

如果你能提出你无法在没有所需方法的情况下解决的问题,可能会有其他答案帮到你。

英文:

The interface has ByNumber too

I assume (I've not used the method) that you could give this 1 from your example.

And Get, of course so that you can enumerate all of the FieldDescriptors.

I think it's not unreasonable to want to reflect (!) the generated (struct) types and enumerate across them but it feels like turtles-all-the-way-down.

It may help solicit other answers if you could present the problem that you're unable to solve without the desired method?

huangapple
  • 本文由 发表于 2021年9月8日 02:07:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/69092776.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定