Home > Back-end >  Storing Configuration structs in an Interface{} and casting it back to original struct
Storing Configuration structs in an Interface{} and casting it back to original struct

Time:11-09

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)?

CodePudding user response:

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
  • Related