1

我正在尝试获取从 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 中的一个字段,实现如下。

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。

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

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
}
4

1 回答 1

3

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

reflect.Value如果你以代替开头,你可以做你想做的事reflect.Type,因为如果你有一个值,你可以检查存储在接口中的值(或其类型)。要将reflect.Value描述符包装在接口值中,您可以使用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: 
于 2019-09-11T09:42:01.983 回答