英文:
Storing Configuration structs in an Interface{} and casting it back to original struct
问题
我尝试做什么
我为App
编写了一个模板
每个App
都有一个配置 - 因此我想将Config
存储为App
结构体的属性。
我的所有配置都存储在JSON
中 - 因此我想提供一个通用的函数来加载JSON
。
const JSON = `{
"Name": "TestConfig"
}`
注意:我尝试使用指针和非指针来解决问题,但两种版本都会产生相同的错误消息 - 下面都包含了这两个版本。
所以我们以App模板为例:
type App struct {
config interface{}
configPtr *interface{}
}
还有一个Configuration
:
// ConfigurationA是Config的实际实现(应用程序A)
type ConfigurationA struct {
Name string
// ... 在这里添加更多属性
}
接下来,我实现了一个函数来从文件中加载配置并将其存储在App中:
func (a *App) GeneralConfigLoader(jsonData string, v interface{}) (err error) {
// 加载json -> 结构体
err = json.Unmarshal([]byte(jsonData), &v)
if err != nil {
fmt.Println("error unmarshalling JSON data")
return err
}
a.config = v
a.configPtr = &v
return nil
}
由于我有一个具有通用加载函数的App,现在应该可以创建一个简单的函数来将空接口转换为正确的Configuration结构体。
// Config - App的配置
func Config(a *App) *ConfigurationA {
var cfg ConfigurationA = a.config.(ConfigurationA)
return &cfg
}
// ConfigPtr - App的配置
func ConfigPtr(a *App) *ConfigurationA {
var i interface{} = *a.configPtr
return i.(*ConfigurationA)
}
如果我将这些总结在一个可执行文件中:
func main() {
var conf ConfigurationA // 用作配置的接口
var a = &App{}
a.GeneralConfigLoader(JSON, conf)
// 报错:接口转换:interface {}是map[string]interface {},而不是main.ConfigurationA
var cfg = Config(a)
fmt.Println("cfg -> ", cfg)
// 报错:接口转换:interface {}是map[string]interface {},而不是*main.ConfigurationA
var cfgPtr = ConfigPtr(a)
fmt.Println("cfgPtr -> ", cfgPtr)
}
应用程序会发生错误(上面的注释部分...)
为什么Go省略了类型信息?
或者更好地说...为什么我不能将配置转换回原来的类型,因为我知道它是什么?
注意
如果我不使用这个通用的加载器,而是创建具体的实现,它确实可以工作!
问题:
我做错了什么?
在Go中是否根本不可能实现这个(我对此表示怀疑)?
英文:
What I try to do
I wrote a template for App
s
Each App
has a config - therefore I want to store the Config
into an attribute on the App
struct
.
All my config
s are stored in JSON
- therefore I want to provide a general func
for loading the JSON
const JSON = `{
"Name": "TestConfig"
}`
NOTE: I tried to solve my problem with and without pointers, both results in the same error message - both versions included below.
So lets take the App-Template as:
type App struct {
config interface{}
configPtr *interface{}
}
and an Configuration
:
// ConfigurationA is an actual implementation of the Config (Application A)
type ConfigurationA struct {
Name string
// ... add more properties here
}
Further I implement a func
to load the Config
from file
and store it in the App
func (a *App) GeneralConfigLoader(jsonData string, v interface{}) (err error) {
// load json -> struct
err = json.Unmarshal([]byte(jsonData), &v)
if err != nil {
fmt.Println("error unmarshalling JSON data")
return err
}
a.config = v
a.configPtr = &v
return nil
}
As I have an App with a general load func
it should now be possible to create a simple func
to cast the empty interface to the correct Configuration struct.
// Config - Config of the App
func Config(a *App) *ConfigurationA {
var cfg ConfigurationA = a.config.(ConfigurationA)
return &cfg
}
// ConfigPtr - Config of the App
func ConfigPtr(a *App) *ConfigurationA {
var i interface{} = *a.configPtr
return i.(*ConfigurationA)
}
If I sum this up in an executable like:
func main() {
var conf ConfigurationA // the interface used as Configuration
var a = &App{}
a.GeneralConfigLoader(JSON, conf)
//panics: interface conversion: interface {} is map[string]interface {}, not main.ConfigurationA
var cfg = Config(a)
fmt.Println("cfg -> ", cfg)
//panics: interface conversion: interface {} is map[string]interface {}, not *main.ConfigurationA
var cfgPtr = ConfigPtr(a)
fmt.Println("cfgPtr -> ", cfgPtr)
}
The application panics (comments in the section above...)
Why does go omit the type information?
Or better... why cant I cast the config back to what it was, as I know what it is...?
NOTICE
Iff I do not use this general loader and create concrete implementations it does work!
Questions:
What am I doing wrong?
Is it simply not possible using go (doubt so)?
答案1
得分: 3
将指针传递给GeneralConfigLoader函数。将其解组到该指针中。删除字段App.configPtr
,它没有用处并且不会按照你的期望工作。
func (a *App) GeneralConfigLoader(jsonData string, v interface{}) (err error) {
err = json.Unmarshal([]byte(jsonData), v) // 这里移除了&
if err != nil {
fmt.Println("error unmarshalling JSON data")
return err
}
a.config = v
return nil
}
func Config(a *App) *ConfigurationA {
return a.config.(*ConfigurationA)
}
像这样加载配置:
var config ConfigurationA
var a = &App{}
a.GeneralConfigLoader(JSON, &config) // 这里添加了&
英文:
Pass a pointer to GeneralConfigLoader. Unmarshal to that pointer. Drop field App.configPtr
. It's not useful and does not do what you expect.
func (a *App) GeneralConfigLoader(jsonData string, v interface{}) (err error) {
err = json.Unmarshal([]byte(jsonData), v) // & removed here
if err != nil {
fmt.Println("error unmarshalling JSON data")
return err
}
a.config = v
return nil
}
func Config(a *App) *ConfigurationA {
return a.config.(*ConfigurationA)
}
Load the configuration like this:
var config ConfigurationA
var a = &App{}
a.GeneralConfigLoader(JSON, &config) // & added here
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论