Mysql 在Go中将SQL行转换为JSON格式

Mysql 在Go中将SQL行转换为JSON格式,mysql,rest,go,Mysql,Rest,Go,在我正在开发的RESTAPI中,有一个/courses端点,它以JSON格式返回从SQL表查询的一些数据。但是,我找不到将查询的数据(行)转换为JSON的方法 func GetCoursesEndpoint(w http.ResponseWriter, req *http.Request) { db, err := sql.Open("mysql", "root:@/academy") checkErr(err) rows, err := db.Query("SELECT cour

在我正在开发的RESTAPI中,有一个
/courses
端点,它以JSON格式返回从SQL表查询的一些数据。但是,我找不到将查询的数据(行)转换为JSON的方法

func GetCoursesEndpoint(w http.ResponseWriter, req *http.Request) {
  db, err := sql.Open("mysql", "root:@/academy")
  checkErr(err)

  rows, err := db.Query("SELECT course_name,price FROM course;")
  checkErr(err)

  //how to convert returned rows to JSON?
  msg, err := json.Marshal(rows)
  checkErr(err)

  json.NewEncoder(w).Encode(msg)
  return  
}
该类型的实例不能直接封送到json中。它不实现
json.Marshaler
接口,其所有字段都未报告,因此
encoding/json
包无法访问

您需要做的是将行的内容扫描到一个可以封送的中间对象中,然后将该对象封送到json中

首先,声明一个表示这个“中间”对象的类型,例如:

type Course struct {
    Name  string
    Price int
}
然后,由于要选择多条记录,因此需要使用其
Next
方法迭代rows对象,并在每次迭代时将记录的内容扫描到
课程
类型的实例中

var courses []*Course // declare a slice of courses that will hold all of the Course instances scanned from the rows object
for rows.Next() { // this stops when there are no more rows
    c := new(Course) // initialize a new instance
    err := rows.Scan(&c.Name, &c.Price) // scan contents of the current row into the instance
    if err != nil {
        return err
    }

    courses = append(courses, c) // add each instance to the slice
}
if err := rows.Err(); err != nil { // make sure that there was no issue during the process
    return err
}
最后,您可以将
courses
片段传递给编码器,从而将其转换为json

if err := json.NewEncoder(w).Encode(courses); err != nil {
    log.Println(err)
}

如果你将上述建议应用到你的处理程序中,你应该开始看到你期望的结果,或者类似的结果。。。但是,如果不希望应用程序崩溃,处理程序还有一些其他问题需要解决

第一:

db, err := sql.Open("mysql", "root:@/academy")
每次执行处理程序时都不需要打开连接,因此,如果将open db connection代码移到处理程序之外,只让处理程序可以访问
db
变量,效果会更好。但是,如果希望每次都继续打开连接,则必须确保每次都关闭连接,否则将耗尽可用的连接

第二:

rows, err := db.Query("SELECT course_name,price FROM course;")
返回的
对象需要关闭,原因与每次继续打开
db
句柄时需要关闭该句柄的原因相同,也就是说,您将耗尽可用连接,并且应用程序将崩溃

因此,应该正确运行的更完整版本的代码如下所示:

var db *sql.DB // declare a global variable that will be used by all handlers

func init() {
    var err error
    db, err = sql.Open("mysql", "root:@/academy") // initialize the global connection
    if err != nil {
        panic(err)
    }
}

type Course struct {
    Name  string
    Price int
}

func GetCoursesEndpoint(w http.ResponseWriter, req *http.Request) {
    rows, err := db.Query("SELECT course_name,price FROM course;")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer rows.Close() // make sure rows is closed when the handler exits

    var courses []*Course
    for rows.Next() {
        c := new(Course)
        err := rows.Scan(&c.Name, &c.Price)
        if err != nil {
            fmt.Println(err)
            return
        }

        courses = append(courses, c)
    }
    if err := rows.Err(); err != nil {
        fmt.Println(err)
        return
    }

    if err := json.NewEncoder(w).Encode(courses); err != nil {
        fmt.Println(err)
    }
    return
}