I have a variables
package in which I have an interface Variable
and two structs which implement the methods on the interface, NumericalVariable
and TextualVariable
, like this...
package variables
type Variable interface {
GetColumnIndex() int
IsActive() bool
StartDataLoad()
AddData(value string) (bool, error)
FinishDataLoad()
VectorLength() int
SetVectorOffset(offset int)
LoadIntoVector(sv string, vector []float64) float64
}
type NumericVariable struct {
Column_idx int
Name string
Vector_offset int
Mean float64
Sum float64
SumSq float64
SD float64
Count float64
Max float64
Min float64
Vector_max float64
Vector_min float64
values []float64
Active bool
}
type Category struct {
Count int
Vector_offset int
Coefficient float64
}
type TextualVariable struct {
Column_idx int
Name string
Vector_offset int
Categories map[string]Category
Categories_k float64
Active bool
Count int
}
I have another module model
which defines a Model
type which includes a slice of Variable
interfaces, like this...
package model
type Model struct {
Vars []variables.Variable
}
In my code elsewhere I am loading and processing a stream/file of data and I create either NumericalVariable
or TextualVariable
instances depending on the data I am presented with. These get added to the Vars
slice on an instance of Model
I want to be able to read and write the Model
struct to a JSON file.
Writing is dead easy, I take advantage of the marshaling in Golang's json
package (so long as I am happy with Capitalized variable names)
func (_self Model) Write(filename string) {
file, err := json.MarshalIndent(_self, "", " ")
if err != nil {
log.Fatal(err)
return
}
err = ioutil.WriteFile(filename, file, 0644)
if err != nil {
log.Fatal(err)
return
}
}
However reading from JSON is proving trickier. The issue is that model.Vars
is a slice of interfaces. The marshaler handles that just fine, but when reading back in I don't have the information about the type that was written.
I thought I could get to this by reflection, and I am nearly there, but I am stuck.
My reader looks like this...
func (_self Model) Read(filename string) {
_model, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatal(err)
return
}
var dat map[string][]interface{}
err = json.Unmarshal([]byte(_model), &dat)
if err != nil {
log.Fatal(err)
return
}
_vars := dat["Vars"]
for _, _vi := range _vars {
k := reflect.ValueOf(_vi)
if k.Kind() == reflect.Map {
if len(k.MapKeys()) == 13 {
// I know this is a numeric variable
nv := variables.NumericVariable()
// How do I get the reflect kind to load up nv
}
if len(k.MapKeys()) == 8 {
// I know this is a textual variable
tv := variables.TextualVariable()
// How do I get the reflect kind to load up tv
}
}
}
I can reliably (if a bit kludgily) detect when I have each type in the reflection, but how do I then get it to load the values into a struct. Ideally I want an automatic unmarshal into the struct variables of the same names. I don't want to have to do it field by field (although I may have to resort to that), but how do I even do that?
CodePudding user response:
directly convert your map[string]interface{} to struct by asserting type to value of map
var nv NumericVariable
for key,value:= range vars {
nv.Column_idx = vars[key].(int) // interface{} will be converted to int
nv.Name = vars[key].(string) // interface{} will be converted to string
..
..
}
Asserting type with convert interface values of map to your given type in struct.
CodePudding user response:
Take a look at the playground link provided in this answer. They are proposing a solution to a similar problem. You could perhaps use this idea to assert the type and unmarshal directly into the type you need after that.