如何在 golang 原型生成复杂结构中获取所有字段名称



我正在尝试获取从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是原型中的字段之一,其实现方式如下。

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"`
}

因为我想在任务操作结构中获取字段名称,即进度。

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

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
}

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

如果从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: %sn", 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: 

最新更新