I am looking for a way to store information which struct a function should use. Each struct corresponds to certain database table.
type Record struct {
TableName string
PrimaryKey string
//XormStruct // how can I store User or Post here?
XormStruct2 interface{} // see I have tried below
XormStruct3 reflect.Type // see I have tried below
}
var posts []Post
var ListOfTables [...]Record {
{"User", "id", User},
//{"Post", "post_id", Post},
{"Post", "post_id", posts, reflect.TypeOf([]Post{})},
}
// User is xorm struct
type User struct {
Id int64
Name string
}
// Post is xorm struct
type Post struct {
Post_id int64
Name string
Other string
}
I want to be able to dynamically choose struct for table.
for _, rec := range ListOfTables {
//var entries []rec.XormStruct // this does not work, of course
//var entries []reflect.TypeOf(rec.XormStruct) // this does not work either
// xorm is *xorm.Engine
xorm.Find(&entries)
//xorm.Find(&rec.XormStruct2) //with interface{}, does not work - produces an empty &[]
posts3 := reflect.New(rec.XormStruct3).Interface()
//xorm.Find(&posts3) // same as above, produces empty &[]
var posts []Post
xorm.Find(&posts) // produces desired data
// afterwards I will do same to any table entries, e.g.
xml.Marshal(entries)
// so there is really no need to create identical functions for each table
}
Goal DRY (I have about 30 tables, function is the same)
I have tried:
to use reflect.TypeOf()
, but I do not understand if/how I can use it (reflect.Type
) to define a new variable
Define Record with XormStruct interface{}
and for each ListOfTables entry create a slice e.g. var posts []Post
and {"Post", "post_id", posts},
Searching SO and godocs
It seems to me, that xorm.Find()
is not "happy" about getting an interface{}
instead of []Posts
even if it does not say so.
UPDATE: I believe the breaking difference is this:
spew.Dump(posts3) //posts3 := reflect.New(rec.XormStruct3).Interface()
// (*[]main.Post)<0x2312312>(<nil>)
spew.Dump(posts) //var posts []Post
// ([]main.Post)<nil>
SOLUTION
posts3 := reflect.New(rec.XormStruct3).Interface()
xorm.Find(posts3) // not &posts3
You may use reflect.Type
to represent / describe a Go type. And at runtime, you may use reflect.New()
to obtain a pointer to the zeroed value of this type wrapped in a reflect.Value
. And if you need a slice instead of a single value, you may use reflect.SliceOf()
, or obtain the type descriptor of the slice value in the first place.
If you store refect.Type
values of your tables, this is how you can use it:
type Record struct {
TableName string
PrimaryKey string
XormStruct reflect.Type
}
var ListOfTables [...]Record {
{"User", "id", reflect.TypeOf((*User)(nil)).Elem()},
{"Post", "post_id", reflect.TypeOf((*Post)(nil)).Elem()},
}
// User is xorm struct
type User struct {
Id int64
Name string
}
// Post is xorm struct
type Post struct {
Post_id int64
Name string
Other string
}
Note you must use exported fields!
And then handling the tables:
for _, rec := range ListOfTables {
entries := reflect.New(reflect.SliceOf(t.XormStruct)).Interface()
err := xorm.Find(entries)
// Handle error
err := xml.Marshal(entries)
// Handle error
}
You can see a working example (proof of concept) of this (without xorm
as that is not available on the Go Playground) using JSON: Go Playground.
If you were to store reflect.Type
values of slices in the first place:
var ListOfTables [...]Record {
{"User", "id", reflect.TypeOf([]User{})},
{"Post", "post_id", reflect.TypeOf([]Post{})},
}
And using it is also simpler:
for _, rec := range ListOfTables {
entries := reflect.New(t.XormStruct).Interface()
err := xorm.Find(entries)
// Handle error
err := xml.Marshal(entries)
// Handle error
}
See proof of concept of this: Go Playground.
Note that if Record
holds slice types (in the field XormStruct
), should you ever need to access the type of the struct (the element type of the struct), you may use Type.Elem()
for that.