存储有关结构的信息/参考

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.