我想从数据库中检索记录,然后把它们发送给json。我有大约30个不同的表,所以我希望泛型函数能够与所有这些表和任何一个表一起工作。我使用xorm进行数据库访问。
我已经成功地创建了检索数据的干函数,主要归功于这个问答。
此工作,可以将所有记录编组到json:
type user struct {
Id int64 `json:"id"`
Name string `json:"name"`
}
// type post
// etc.
type tableRecord struct {
PrimaryKey string
Data interface{}
}
var ListOfTables = map[string]tableRecord{
"users":{"id", &[]user{}}, // type user is struct for xorm with json annotation
//"posts":{"post_id", &[]post{}},
// etc..
}
for tableName, rec := range ListOfTables {
err := xorm.Find(rec.Data)
if err != nil {
log.Print(err)
}
out, err := json.Marshal(rec.Data)
if err != nil {
log.Print(err)
}
log.Print(string(out)) // this yields json array
}然而,我很难将一张唱片编成json。我一直在寻找迭代一个包含片、找到这个和类似主题的接口{}的方法。试过:
switch reflect.TypeOf(reflect.ValueOf(rec.Data).Elem().Interface()).Kind() {
case reflect.Slice:
s := reflect.ValueOf(reflect.ValueOf(rec.Data).Elem().Interface())
for i := 0; i < s.Len(); i++ {
entry := s.Index(i)
log.Printf("%v\n", entry) // prints {1 John Doe}
// log.Print(reflect.ValueOf(entry))
data, err := json.MarshalIndent(entry, " ", " ")
if err != nil {
log.Print(err)
}
log.Println(string(data)) // prints {} empty
}
} 当然,如果我指定rec.Data是*[]user,那么它可以工作,但是我必须为每个表重写这样的代码,这不是我想要的。
switch t := rec.Data.(type) {
case *[]user:
for _, entry := range *t {
// log.Printf("loop %v", entry)
data, err := json.MarshalIndent(entry, " ", " ")
if err != nil {
log.Print(err)
}
log.Println(string(data)) // yields needed json for single record
}
}或者,也许有一种完全不同的、更好的方法来解决这些问题--任何数据库到json的记录。
更新现在的问题是,Xorm期望结构吗?我将不得不阅读xorm的可能性和限制。
slice := record.Slice()
log.Print(reflect.TypeOf(slice))
err = env.hxo.In(record.PrimaryKey(), insertIds).Find(slice) // or &slice
if err != nil {
log.Print(err) // Table not found
}
// this works
var slice2 []*user
err = env.hxo.In(record.PrimaryKey(), insertIds).Find(&slice2)
if err != nil {
log.Print(err) //
}发布于 2018-12-14 11:58:32
因此,正如我在注释中提到的,如果您想从tableRecord.Data字段中获取单个元素,最简单的方法就是将字段类型更改为实际情况:
type tableRecord struct {
PrimaryKey string
Data []interface{} // slice of whatever
}这样,您就可以编写一些非常通用的东西:
for tbl, record := range records {
fmt.Printf("First record from table %s\n", tbl)
b, _ := json.MarshalIndent(record[0], " ", " ")
fmt.Println(string(b))
fmt.Prinln("other records...")
b, _ = json.MarshalIndend(record[1:], " ", " ")
fmt.Println(string(b))
}但是,如果我是您,我会考虑在我的DB类型中实现一个接口。与…有关的东西:
type DBType interface {
PrimaryKey() string
TableName() string // xorm can use this to get the table name
Slice() []DBType // can return []user or whatever
}因此,您不再需要tableRecord类型了,只需使用这样的var:
listOfTables := []DBType{user{}, ...}
for _, tbl := range listOfTables {
data := tbl.Slice()
// find data here
fmt.Printf("First record from table %s\n", tbl.TableName())
b, _ := json.MarshalIndent(data[0], " ", " ")
fmt.Println(string(b))
fmt.Prinln("other records...")
b, _ = json.MarshalIndend(data[1:], " ", " ")
fmt.Println(string(b))
}因此,我的回答/评论中缺少的是TL;DR:
[]user{} (或[]DBTable)到[]interface{}的转换不起作用,因为您不能在一个表达式中将所有元素转换成一个片段。您必须创建第二个[]interface{}类型的片段,并复制如下所示的值:
:= userVar.Slice() data := make([]接口{},len( := ))用于I:=范围片{ datai =样片//复制类型到接口{}:=}返回tableRecord{userVar.PrimaryKey(),数据}我已经创建了一个关于如何使用上面描述的接口的小示例。
为了避免太多的混乱,您可以更改Slice功能,立即返回一个[]interface{}:
func(v T) Slice() []interface{
return []interface{
&T{},
}
}实现Slice的错误之处在于,您有这样的东西:
func (u *user) Slice() []DBTable {
u = &user{} // you're re-assigning the receiver, losing all state!
return []DBTable{u}
}接收器是一个指针类型,所以您正在进行的任何重新分配都将影响调用func的变量。这不是个好主意。只需使用值接收器,或者,如果您想确保接口只在指针变量上实现(例如,gRPC使用的一个常见技巧)就是这样实现该函数:
func(*user) Slice() []DBTable{
return []DBTable{&user{}}
}在使用协议缓冲区时,可以在生成的pb.go文件中找到这个技巧的一个很好的例子。消息类型将具有如下函数:
func(*MsgType) ProtoMessage() {}https://stackoverflow.com/questions/53777722
复制相似问题