如何获取golang proto生成的复杂结构中的所有字段名称

How to get all Fields names in golang proto generated complex structs

我正在尝试获取从 proto 生成的 go 文件中的所有字段名称。 下面是生成的结构。

type Action struct {
    Name             string            `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
    // Types that are valid to be assigned to ActionType:
    //  *Action_TaskAction
    ActionType           isAction_ActionType `protobuf_oneof:"action_type"`
}

可以看出,ActionType是proto中的一个Field,实现如下。

type isAction_ActionType interface {
    isAction_ActionType()
}

type Action_TaskAction struct {
    TaskAction *TaskAction `protobuf:"bytes,16,opt,name=task_action,json=taskAction,proto3,oneof"`
}

type TaskAction struct {
    Progress             float32  `protobuf:"fixed32,1,opt,name=progress,proto3" json:"progress,omitempty"`
}

因为我想获取 TaskAction 结构中的字段名称,即 Progress。

我正在使用下面的代码来获取字段名称,但如果字段类型是接口(对于其中一个字段)就会遇到问题

func printFieldNames(t reflect.Type) error {
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        if field.Type.Kind() == reflect.Struct {
            printFieldNames(field.Type)
            continue
        }
        if field.Type.Kind() == reflect.Interface {
            // what to do here.
        }
        column := field.Tag.Get("json")
        fmt.Println("column: ", column)
    }
    return nil
}

如果类型是接口,您就无能为力了。在实际值中,它可能是实现该接口的结构或任何其他类型,但接口 type 本身不能告诉你这一点,它不限制具体类型。

如果你从reflect.Value instead of reflect.Type, because if you have a value, you can examine the value (or its type) that is stored in the interface. To get the reflect.Value descriptor wrapped in an interface value, you may use reflect.Elem()开始,你可以做你想做的事。

此外,要处理指向结构的指针,您可以再次使用 reflect.Elem() 来获取指向的值。您可以通过将其种类与 reflect.Ptr.

进行比较来检查值是否为指针

这是您的 printFieldNames() 的示例,重写后可与 reflect.Value 一起使用,它递归到存储在接口值中的结构中。这不是一个可以处理所有情况的解决方案,而是演示了如何做到这一点:

func printFieldNames(v reflect.Value) {
    for i := 0; i < v.NumField(); i++ {
        field := v.Field(i)
        if field.Kind() == reflect.Ptr {
            field = field.Elem()
        }
        if field.Kind() == reflect.Struct {
            printFieldNames(field)
            continue
        }
        if field.Kind() == reflect.Interface {
            wrapped := field.Elem()
            if wrapped.Kind() == reflect.Ptr {
                wrapped = wrapped.Elem()
            }
            printFieldNames(wrapped)
        }
        structfield := v.Type().Field(i)
        column := structfield.Tag.Get("json")
        fmt.Printf("column: %s, json tag: %s\n", structfield.Name, column)
    }
}

正在测试:

a := Action{
    ActionType: Action_TaskAction{
        TaskAction: &TaskAction{},
    },
}
printFieldNames(reflect.ValueOf(a))

输出将是(在 Go Playground 上尝试):

column: Name, json tag: name,omitempty
column: Progress, json tag: progress,omitempty
column: ActionType, json tag: