首页 > 解决方案 > 在反射中设置结构的值时遇到问题

问题描述

假设我有一个由其他两个组合而成的基本结构:

type Config struct {
    common.Config
    common.ServerConfig
    APIPath      string `type:"string" name:"apipath" default:"localhost:8443" desc:"Path to the gRPC API server"`
    ServePath    string `type:"string" name:"servepath" default:"/" desc:"Path to serve the API from"`
    Organization string `type:"string" name:"organization" default:"" desc:"Default organization name"`
}

我想遍历结构中的每个字段并将值拉入结构中:

func NewConfig(c Configer) {
    setConfigFlags(reflect.TypeOf(c).Elem(), c.GetViper())
}

所以我会这样称呼它:

conf := Config{}
common.NewConfig(&conf)

我的 setConfigFlags 正在做一些递归:

func setConfigFlags(t reflect.Type, viper *viper.Viper) {
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        switch field.Type.Kind() {
        case reflect.Struct:
            setConfigFlags(field.Type, viper)
            continue
        case reflect.String:
            fieldValue := reflect.ValueOf(&field).Elem()
            if !fieldValue.IsValid() || !fieldValue.CanSet() {
                return
            }
            fieldValue.SetString(
                viper.GetString(field.Tag.Get("name")),
            )
        }
    }
}

现在,一旦我将 SetString 线打到底部,我就会收到此错误:

panic: reflect: call of reflect.Value.SetString on struct Value

当我当时查看 fieldValue 的类型时,它是一个 structfield。我在尝试解决该值并更改该字段的值时遇到了麻烦。我相信我没有正确获取实际结构字段的地址。示例和文档肯定没有帮助。

在这个特定的示例中,我正在提取 viper 值。基本上寻找一种通过结构标签定义我的毒蛇定义的方法,这是最终目标。

标签: go

解决方案


正如 mkopriva 的评论所指出的那样,该代码正试图在一个类型上设置一个值。使用一个值。

func setConfigFlags(v reflect.Value, viper *viper.Viper) {
    t := v.Type()
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        switch field.Type.Kind() {
        case reflect.Struct:
            setConfigFlags(v.Field(i), viper)
            continue
        case reflect.String:
            v.Field(i).SetString(viper.GetString(field.Tag.Get("name")))
        }
    }
}

像这样称呼它:

func NewConfig(c Configer) {
    setConfigFlags(reflect.Value(c).Elem(), c.GetViper())
}

推荐阅读