将配置结构体存储在Interface{}中,并将其转换回原始结构体。

huangapple go评论82阅读模式
英文:

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 Apps
Each App has a config - therefore I want to store the Config into an attribute on the App struct.
All my configs 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

huangapple
  • 本文由 发表于 2021年11月8日 23:49:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/69886287.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定