将接口转换为定义的结构 Golang

convert interface to defined struct Golang

提问人:Hemant Yadav 提问时间:12/5/2022 更新时间:12/5/2022 访问量:102

问:

我正在研究一个实用程序方法,比如说 GetConfig(),它读取配置结构并将其返回给调用者。GetConfig() 不知道它将读取什么配置,但调用者知道结构体将接收什么。

在这方面,我编写了一个以下实用程序:

=========== yaml file data ==========
apiRouting:
  enableThrottling: true
  formFactor: 4
leasing:
  periodInSecs: 10
  preemptionEnable: false
=========== yaml file data ==========

func GetConfig() (interface{}, error) {
    fmt.Println("reading generic service config")
    viper.SetConfigName("service_config")
    viper.AddConfigPath("config/default")
    if err := viper.ReadInConfig(); err != nil {
        return nil, err
    }
    var appConfig interface{}
    if err := viper.Unmarshal(&appConfig); err != nil {
        return nil, err
    }
    return appConfig, nil
}

调用者像这样使用 GetConfig():(我尝试了 2 个选项,没有任何效果)

type ApiRouting struct {
    EnableThrottling bool  `json:"enableThrottling"`
    FormFactor       int32 `json:"formFactor"`
}

type Leasing struct {
    PeriodInSecs     int32 `json:"periodInSecs"`
    PreemptionEnable bool  `json:"preemptionEnable"`
}

type ServiceConfig struct {
    ApiRouting ApiRouting `json:"apiRouting"`
    Leasing    Leasing    `json:"leasing"`
}

// code snipped [option 1]
    tmpinterface := GetConfig()
    myconfig, ok := tmpinterface.(ServiceConfig)
    if !ok {
        log.Fatal()
    } else {
        println(myconfig)
    }

// code snipped [option 2]
    tmpinterface := GetConfig()
    // Convert map to json string
    jsonStr, err := json.Marshal(tmpinterface)
    if err != nil {
        fmt.Println(err)
    }
    
    // Convert json string to struct
    var sc ServiceConfig
    if err := json.Unmarshal(jsonStr, &sc); err != nil {
        fmt.Println(err)
    }

我已经验证了在这两种情况下都正确获取了值,但最终结构为空。tmpinterfacemyconfig{}

tmpinterface值为:

map[%!f(string=apirouting):map[%!f(string=enablethrottling):%!f(bool=true) %!f(string=formfactor):%!f(int=4)] %!f(string=leasing):map[%!f(string=periodinsecs):%!f(int=10) %!f(string=preemptionenable):%!f(bool=false)]]
Go Struct 接口 转换

评论

2赞 mkopriva 12/5/2022
GetConfig应该接受具体配置类型的实例,即 .var sc ServiceConfig; GetConfig(&sc)
0赞 Hemant Yadav 12/5/2022
@mkopriva:我不能在这里使用具体类型。因为代码位于框架中,各种服务都会调用它。框架实际上并不需要知道具体类型。我非常接近我的解决方案,只是我无法将其解组到所需的结构中。GetConfig
0赞 mkopriva 12/5/2022
GetConfig不需要知道具体类型,它可以接受作为参数,就像 or 一样。anyjson.Unmarshalviper.Unmarshal
1赞 mkopriva 12/5/2022
像这样的东西:go.dev/play/p/jshA4lk3Ero
1赞 Hemant Yadav 12/5/2022
@mkopriva:真的很感谢,任何作品都像魅力一样,而且看起来也很干净。今天学到了一件好事。

答:

1赞 Hemant Yadav 12/5/2022 #1

@mkopriva,感谢您提供更清洁的解决方案。

func GetConfig(appConfig any) error {
    fmt.Println("reading generic service config")
    viper.SetConfigName("service_config")
    viper.AddConfigPath("config/default")
    if err := viper.ReadInConfig(); err != nil {
        return err
    }
    if err := viper.Unmarshal(appConfig); err != nil {
        return err
    }
    return nil
}

func main() {
    var sc ServiceConfig
    if err := GetConfig(&sc); err != nil {
        panic(err)
    }
    fmt.Println(sc)
}