在Golang中,将数据库行转换为JSON有简单的方法吗?

huangapple go评论85阅读模式
英文:

Is there a simple way to convert data base rows to JSON in Golang

问题

根据你提供的代码,我理解你想要将数据库的行转换为JSON或[]map[string]interface{}的格式,但这个过程并不简单。你需要创建两个切片,并且需要循环遍历列,每次都要创建键。

你想知道是否有适用于这种情况的包,或者是否有一些基础知识我没有考虑到。

根据我了解,Go语言中有一些用于处理数据库查询结果的包,例如database/sqlencoding/json。你可以使用database/sql包来执行查询并获取结果集,然后使用encoding/json包将结果集转换为JSON格式。

以下是一个示例代码,演示了如何使用这两个包来实现你的需求:

import (
    "database/sql"
    "encoding/json"
    "fmt"
)

func main() {
    // 假设你已经建立了数据库连接,并执行了查询操作
    rows, err := db.Query("SELECT * FROM your_table")
    if err != nil {
        fmt.Println("查询出错:", err)
        return
    }
    defer rows.Close()

    // 获取列信息
    columns, err := rows.Columns()
    if err != nil {
        fmt.Println("获取列信息出错:", err)
        return
    }

    // 创建一个切片来存储结果
    tableData := make([]map[string]interface{}, 0)

    // 遍历每一行数据
    for rows.Next() {
        // 创建一个切片来存储每一列的值
        values := make([]interface{}, len(columns))
        // 创建一个切片来存储每一列的指针
        valuePtrs := make([]interface{}, len(columns))
        for i := range columns {
            valuePtrs[i] = &values[i]
        }

        // 扫描每一行的数据到切片中
        if err := rows.Scan(valuePtrs...); err != nil {
            fmt.Println("扫描数据出错:", err)
            return
        }

        // 创建一个map来存储每一行的数据
        entry := make(map[string]interface{})
        for i, col := range columns {
            // 将每一列的值转换为合适的类型
            val := values[i]
            b, ok := val.([]byte)
            if ok {
                entry[col] = string(b)
            } else {
                entry[col] = val
            }
        }

        // 将每一行的数据添加到结果集中
        tableData = append(tableData, entry)
    }

    // 将结果集转换为JSON格式
    jsonData, err := json.Marshal(tableData)
    if err != nil {
        fmt.Println("转换为JSON格式出错:", err)
        return
    }

    // 打印JSON数据
    fmt.Println(string(jsonData))
}

请注意,这只是一个示例代码,你需要根据你的实际情况进行适当的修改和调整。希望对你有帮助!

英文:

Currently on what I've seen so far is that, converting database rows to JSON or to []map[string]interface{} is not simple. I have to create two slices and then loop through columns and create keys every time.

...Some code

tableData := make([]map[string]interface{}, 0)
values := make([]interface{}, count)
valuePtrs := make([]interface{}, count)
for rows.Next() {
for i := 0; i < count; i++ {
valuePtrs[i] = &values[i]
}
rows.Scan(valuePtrs...)
entry := make(map[string]interface{})
for i, col := range columns {
var v interface{}
val := values[i]
b, ok := val.([]byte)
if ok {
v = string(b)
} else {
v = val
}
entry[col] = v
}
tableData = append(tableData, entry)
}

Is there any package for this ? Or I am missing some basics here

答案1

得分: 7

我正在处理相同的问题,根据我的调查,似乎没有其他方法

我看到的所有包基本上都使用相同的方法。

有几件事情你应该知道,希望能节省你的时间

  • database/sql 包将所有数据转换为适当的类型。
  • 如果你使用的是 mysql 驱动(go-sql-driver/mysql),你需要在数据库字符串中添加参数,以便返回时间类型而不是字符串(使用 ?parseTime=true,默认为 false)。

你可以使用社区编写的工具来减轻负担:

  • 一个简约的 database/sql 包的封装,sqlx,在内部使用反射方式类似地处理。
  • 如果你需要更多功能,可以尝试使用一个“orm”:gorpgorm

如果你对深入了解感兴趣,请查看:

  • 在 sqlx 包中使用反射,sqlx.go 的第 560 行。

  • 在 database/sql 包中的数据类型转换,convert.go 的第 86 行。

英文:

I'm dealing with the same issue, as far as my investigation goes it looks that there is no other way.

All the packages that I have seen use basically the same method

Few things you should know, hopefully will save you time:

  • database/sql package converts all the data to the appropriate types
  • if you are using the mysql driver(go-sql-driver/mysql) you need to add
    params to your db string for it to return type time instead of a string
    (use ?parseTime=true, default is false)

You can use tools that were written by the community, to offload the overhead:

  • A minimalistic wrapper around database/sql, sqlx, uses similar way internally with reflection.

  • If you need more functionality, try using an "orm": gorp, gorm.

If you interested in diving deeper check out:

  • Using reflection in sqlx package, sqlx.go line 560
  • Data type conversion in database/sql package, convert.go line 86

答案2

得分: 2

你可以创建一个模拟你的数据的结构体。

**注意:我正在使用的是MS SQLServer。

假设你想获取一个用户:

type User struct {
    ID       int    `json:"id,omitempty"`
    UserName string `json:"user_name,omitempty"`
    ...
}

然后你可以这样做:

func GetUser(w http.ResponseWriter, req *http.Request) {
    var u User
    params := mux.Vars(req)

    db, err := sql.Open("mssql", "server=ServerName")
    if err != nil {
        log.Fatal(err)
    }

    err1 := db.QueryRow("select Id, UserName from [Your Datavse].dbo.Users where Id = ?", params["id"]).Scan(&u.ID, &u.UserName)
    if err1 != nil {
        log.Fatal(err1)
    }
    json.NewEncoder(w).Encode(&u)

    if err != nil {
        log.Fatal(err)
    }
}

这是我使用的导入语句:

import (
    "database/sql"
    "net/http"

    "log"

    "encoding/json"

    _ "github.com/denisenkom/go-mssqldb"
    "github.com/gorilla/mux"
)

这样可以从数据库中获取数据并将其转换为JSON。

这需要一些时间来编写,但效果非常好。

英文:

One thing you could do is create a struct that models your data.

**Note: I am using MS SQLServer

So lets say you want to get a user

type User struct {
ID int `json:"id,omitempty"`
UserName string `json:"user_name,omitempty"`
...
}

then you can do this

func GetUser(w http.ResponseWriter, req *http.Request) {
var r Role
params := mux.Vars(req)
db, err := sql.Open("mssql", "server=ServerName")
if err != nil {
log.Fatal(err)
}
err1 := db.QueryRow("select Id, UserName from [Your Datavse].dbo.Users where Id = ?", params["id"]).Scan(&r.ID, &r.Name)
if err1 != nil {
log.Fatal(err1)
}
json.NewEncoder(w).Encode(&r)
if err != nil {
log.Fatal(err)
}
}

Here are the imports I used

import (
"database/sql"
"net/http"
"log"
"encoding/json"
_ "github.com/denisenkom/go-mssqldb"
"github.com/gorilla/mux"
)

This allowed me to get data from the database and get it into JSON.

This takes a while to code, but it works really well.

答案3

得分: 2

在Go发行版本身中没有,但有一个很棒的jmoiron/sqlx库:

import "github.com/jmoiron/sqlx"

tableData := make([]map[string]interface{}, 0)

for rows.Next() {
  entry := make(map[string]interface{})

  err := rows.MapScan(entry)

  if err != nil {
    log.Fatal("SQL error: " + err.Error())
  }
      
  tableData = append(tableData, entry)
}
英文:

Not in the Go distribution itself, but there is the wonderful jmoiron/sqlx:

import "github.com/jmoiron/sqlx"

tableData := make([]map[string]interface{}, 0)
for rows.Next() {
entry := make(map[string]interface{})
err := rows.MapScan(entry)
if err != nil {
log.Fatal("SQL error: " + err.Error())
}
tableData = append(tableData, entry)
}

答案4

得分: 1

如果您知道正在读取的数据类型,那么您可以在不使用通用接口的情况下读取到该数据类型中。

否则,无论使用什么语言,都没有解决方案,这是由于JSON本身的特性所决定的。

JSON没有描述复合数据结构的能力。换句话说,JSON是一个通用的键值结构。当解析器遇到应该是特定结构的内容时,在JSON本身中没有对该结构进行标识。例如,如果您有一个名为User的结构,解析器将不知道一组键值对如何映射到您的User结构。

通常可以通过文档模式(在XML世界中称为XSD)或通过传递预期的数据类型来解决类型识别的问题。

英文:

If you know the data type that you are reading, then you can read into the data type without using generic interface.

Otherwise, there is no solution regardless of the language used due to nature of JSON itself.

JSON does not have description of composite data structures. In other words, JSON is a generic key-value structure. When parser encounters what is supposed to be a specific structure there is no identification of that structure in JSON itself. For example, if you have a structure User the parser would not know how a set of key-value pairs maps to your structure User.

The problem of type recognition is usually addressed with document schema (a.k.a. XSD in XML world) or explicitly through passed expected data type.

答案5

得分: 0

一种快速获取任意和通用的 []map[string]interface{} 结果的方法是,使用与查询中的列数相同大小的接口指针数组,并将其作为参数传递给 scan 函数:

例如,对于 go-mssqldb 库:

queryResponse, err := d.pool.Query(query)
if err != nil {
    return nil, err
}
defer queryResponse.Close()

// 存储所有结果
results := []map[string]interface{}{}

// 获取查询结果中所有字段的详细信息
fieldNames, err := queryResponse.Columns()
if err != nil {
    return nil, err
}

// 在一个与列数相同大小的接口类型指针数组中创建接口类型指针,
// 这样我们就可以将其正确地传递给 "Scan" 函数并获取所有查询参数 :)
var scanResults []interface{}
for range fieldNames {
    var v interface{}
    scanResults = append(scanResults, &v)
}

// 将查询结果解析到结果映射中
for queryResponse.Next() {

    // 这个变量将保存所有列的值,以列名命名
    rowValues := map[string]interface{}{}

    // 清除旧值以防万一
    for _, column := range scanResults {
        *(column.(*interface{})) = nil
    }

    // 扫描到指针数组中
    err := queryResponse.Scan(scanResults...)
    if err != nil {
        return nil, err
    }

    // 将指针映射回其值和相关的列名
    for index, column := range scanResults {
        rowValues[fieldNames[index]] = *(column.(*interface{}))
    }
    results = append(results, rowValues)
}

return results, nil

以上是翻译好的内容,请查阅。

英文:

One quick way to go about being able to get an arbirtrary and generic []map[string]interface{} from these query libraries is to populate an array of interface pointers with the same size of the amount of columns on the query, and then pass that as a parameter on the scan function:

// For example, for the go-mssqldb lib:
queryResponse, err := d.pool.Query(query)
if err != nil {
return nil, err
}
defer queryResponse.Close()
// Holds all the end-results
results := []map[string]interface{}{}
// Getting details about all the fields from the query
fieldNames, err := queryResponse.Columns()
if err != nil {
return nil, err
}
// Creating interface-type pointers within an array of the same
// size of the number of columns we have, so that we can properly
// pass this to the "Scan" function and get all the query parameters back :)
var scanResults []interface{}
for range fieldNames {
var v interface{}
scanResults = append(scanResults, &v)
}
// Parsing the query results into the result map
for queryResponse.Next() {
// This variable will hold the value for all the columns, named by the column name
rowValues := map[string]interface{}{}
// Cleaning up old values just in case
for _, column := range scanResults {
*(column.(*interface{})) = nil
}
// Scan into the array of pointers
err := queryResponse.Scan(scanResults...)
if err != nil {
return nil, err
}
// Map the pointers back to their value and the associated column name
for index, column := range scanResults {
rowValues[fieldNames[index]] = *(column.(*interface{}))
}
results = append(results, rowValues)
}
return results, nil

huangapple
  • 本文由 发表于 2016年9月25日 16:52:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/39685036.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定