为了账号安全,请及时绑定邮箱和手机立即绑定

Golang通用数据库单条记录转json

Golang通用数据库单条记录转json

Go
倚天杖 2023-05-15 15:32:50
我想从数据库中检索记录并将其编组为 json。我有大约 30 个不同的表,所以我想要适用于所有这些表的通用函数。我使用 xorm 进行数据库访问。我设法创建了检索数据的 DRY 函数,这主要归功于这个问答这有效,可以将所有记录编组为 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。我四处寻找迭代 interface{} 的方法,它包含一个切片,找到了这个和类似的主题。试过: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。
查看完整描述

1 回答

?
慕标琳琳

TA贡献1830条经验 获得超9个赞

因此,正如我在评论中提到的,如果您希望能够从字段中获取单个元素,最简单的方法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))

}

不过,如果我是你,我会考虑在我的数据库类型中实现一个接口。类似的东西:


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{}从 type (or []DBTable) to 的转换[]interface{}不起作用,因为您不能在单个表达式中转换切片中的所有元素。您必须创建第二个类型切片[]interface{}并像这样复制值:


slice := userVar.Slice() data := make([]interface{}, len(slice)) for i := range slice { data[i] = slice[i] // 复制类型到 interface{} slice } 返回表记录{userVar.PrimaryKey(), 数据}


我已经创建了一个小的工作示例来说明如何使用上述接口。


演示

为避免太多混乱,您可以更改Slicefunc 以[]interface{}立即返回 a:


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() {}


查看完整回答
反对 回复 2023-05-15
  • 1 回答
  • 0 关注
  • 128 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信