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

标签 go struct protocol-buffers proto protobuf-go

我正在尝试获取从 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。

我使用下面的代码来获取字段名称,但如果字段类型是接口(interface)(对于 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
}

最佳答案

如果类型是接口(interface),你就不能做太多事情。在实际值中,它可能是一个结构体或实现该接口(interface)的任何其他类型,但接口(interface) type 本身无法告诉您这一点,它不限制具体类型。

如果你从 reflect.Value 开始,你可以做你想做的事而不是reflect.Type ,因为如果您有一个值,您可以检查存储在接口(interface)中的值(或其类型)。要获取封装在接口(interface)值中的 reflect.Value 描述符,您可以使用 reflect.Elem() .

此外,要处理指向结构的指针,您可以再次使用 reflect.Elem() 来获取指向的值。您可以通过将其类型与 reflect.Ptr 进行比较来检查某个值是否为指针。

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

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: 

关于go - 如何获取 golang proto 生成的复杂结构中的所有字段名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57885851/

相关文章:

c - 奇怪的 makefile 构建错误

c++ - 如何在头文件中的结构中为数组初始化和分配内存?

c - 在另一个结构中声明/定义的结构范围

c - void *array = *(void **) member + siz * (*p_n); 的目的是什么?

google-app-engine - Google App Engine 内存缓存安全吗?

function - 我用 go 编写的递归函数有什么问题?

go - 如何设置 GOPRIVATE 环境变量

go - 如何生成struct lowerCamelCase json标签

java - 使用 Protocol Buffer 和 Netty 4.1.6

go - 在包含 channel 的 Go case 语句中,阻塞发生在哪里?