Golang中SQL数值格式化JSON的方法

Golang中SQL数值格式化JSON的方法 我在使用sqlx时遇到了格式化问题

rows, err = db.Queryx(query)
for rows.Next() {
    results := make(map[string]interface{})
    err = rows.MapScan(results)
    fmt.Fprintf(w,"%#v \n", results)
}

使用 %#v 时的输出

fmt.Fprintf(w,"%#v \n", results)

map[string]interface {}{“USER_ID”:“JD”, “USER_NAME”:“John Doe”}

map[string]interface {}{“USER_ID”:“JAD”, “USER_NAME”:“Jane Doe”}

map[string]interface {}{“USER_ID”:“DD”, “USER_NAME”:“Donald Duck”}

仅使用 %v

fmt.Fprintf(w,"%v \n", results)

map[USER_ID:JD USER_NAME:John Doe]

map[USER_ID:JAD USER_NAME:Jane Doe]

map[USER_ID:DD USER_NAME:Donald Duck]

期望的输出是去掉 map[string]interface {}

{“USER_ID”:“JD”, “USER_NAME”:“John Doe”}

{“USER_ID”:“JAD”, “USER_NAME”:“Jane Doe”}

{“USER_ID”:“DD”, “USER_NAME”:“Donald Duck”}

使用sqlx能否实现这个效果?


更多关于Golang中SQL数值格式化JSON的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

19 回复

你在说什么?我给了你两个例子,rows.Scan 接受的是指针数组,而不是值数组

更多关于Golang中SQL数值格式化JSON的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我被警告过不要这样做。但是手动输入成千上万个结构体可能会引发更大的恐慌 😊

vitr:

les, rows.Scan 接受一个指针数组,而不是值

是的,谢谢。但我还没弄清楚使用它们的上下文。

我一定做错了什么,得到了重复的值:

[“1”:“1”,“John Doe”:“John Doe”,“6.00”:“6.00”]

感谢提供的链接,可能非常有用。

“我宁愿使用严格的类型定义…”。对于未知表格,我该如何实现这一点?

“…或者使用第三方库进行类型转换…” - 有什么建议吗?

当你确切知道要查找什么时,查阅手册非常有价值。由于Go Playground中没有可运行的SQL示例(据我所发现的),因此很难通过实践来测试和学习。这有点像第22条军规的情况。

只是一个建议。如果没有特别充分的理由将结果放入映射中,请尽量避免这样做。映射是动态结构,必须使用互斥锁进行保护,否则在并发环境中会出现恐慌。就SQL查询而言,这会不必要地暂时减慢操作速度。建议改用扫描和结构体。

rows.Columns() 返回一个字符串切片 []string,你应该直接使用 column,而不是 column.Name()

// 示例代码
func example() {
    columns, _ := rows.Columns()
    for _, column := range columns {
        fmt.Println(column)
    }
}

请学习如何正确使用 rows.Scan 方法:https://golang.org/pkg/database/sql/#Rows,以下是我的示例:

	cols := make([]interface{}, len(colNames))
	colPtrs := make([]interface{}, len(colNames))
	for i := 0; i < len(colNames); i++ {
		colPtrs[i] = &cols[i]
	}

	err = rows.Scan(colPtrs...)

如果您知道表名(构建查询时必须知道),可以使用信息模式(infoschema)来获取表列信息 https://www.postgresql.org/docs/current/static/infoschema-columns.html。在那里您可以找到有关列类型的有用信息,这使您有机会将二进制数据转换为正确的类型。

这是一项繁琐的工作,我宁愿使用严格的类型定义或第三方库进行类型转换。

这很好地回答了我的问题。

但是,问题比我的原始问题更复杂。当使用 make(map[string]interface{}) 从 PostgreSQL 获取数据时,数值列被当作 Base64 处理,变得难以阅读。

这似乎是因为 Golang 的 database/sql 包或 Golang PostgreSQL 驱动程序将数值类型(现在是 []uint8)呈现给 JSON 的方式造成的。

目前我正在关注并研究这个讨论

你说得对。但碰巧我把你的代码放到了一个上下文中。它几乎可以正常工作了!

var rows *sql.Rows
rows, err = db.Query(query)
cols, _ := rows.Columns()
vals := make([]interface{}, len(cols))
for i, _ := range cols {
   vals[i] = &cols[i]
}
for rows.Next() {
   err = rows.Scan(vals...)
   json, _ := json.Marshal(vals)
   fmt.Fprintf(w,"%s\n", json)
}

[“1”,“John Doe”,“6.00”]

但是我该如何获取列名呢?

[“ID”:“1”,“NAME”:“John Doe”,“QTY”:“6.00”]

只有在首次获取时才会不知道表结构)) 有人在数据库中创建了表,你必须将这些定义复制到代码中,开玩笑的)) Sqlboiler可以为你生成所有内容 https://github.com/volatiletech/sqlboiler 也许这些代码库也能提供帮助 https://github.com/mailru/dbr https://github.com/jmoiron/modl 一些类似的Stack Overflow回答 https://stackoverflow.com/questions/17845619/how-to-call-the-scan-variadic-function-in-golang-using-reflection/17885636#17885636 https://stackoverflow.com/questions/42774467/how-to-convert-sql-rows-to-typed-json-in-golang

将列名循环移到行循环外部解决了问题。谢谢!

var rows *sql.Rows
rows, err = db.Query(query)
cols, _ := rows.Columns()
colnames, _ := rows.Columns()
vals := make([]interface{}, len(cols))

for i, _ := range cols {
   vals[i] = &cols[i]
}

mymap := make(map[string]interface{})

for i, val := range vals {
  mymap[colnames[i]] = val
}
 
for rows.Next() {
   err = rows.Scan(vals...)
   json, _ := json.Marshal(mymap)
   fmt.Fprintf(w,"%s\n",json)
}

这(理论上)应该能正常工作,将整数和数值转换为文本。但显然它没有正常工作,所以我想知道有没有人看出我哪里做错了?

  var rows *sql.Rows 
  rows, err = db.Query(query)
  object := map[string]interface{}{}
  columns, err := rows.Columns()
  values := make([]interface{}, len(columns))

  for rows.Next() {
    err = rows.Scan(values...)
    for i, column := range columns {
      object[column.Name()] = new(*string)
      values[i] = object[column.Name()]
    }
    data, _ := json.Marshal(object)
    fmt.Fprintf(w,"%s\n", data)
  }

出现这个错误:

column.Name undefined (type string has no field or method Name)

我到底哪里做错了?

在转换为JSON之前需要一个映射

var rows *sql.Rows
rows, err = db.Query(query)
cols, _ := rows.Columns()
colNames, _ := rows.Columns()
vals := make([]interface{}, len(cols))
for i, _ := range cols {
	vals[i] = &cols[i]
}
for rows.Next() {
	var myMap = make(map[string]interface{})
	err = rows.Scan(vals...)
	for i, v := range vals {
		myMap[colNames[i]] = v
	}
	json, _ := json.Marshal(myMap)
	fmt.Fprintf(w,"%s\n", json)
}

添加了 colNames, _ := rows.Columns()myMap 和一个 for 循环

是的,那个错误消失了 😊 但是没有显示任何值

  var rows *sql.Rows 
  rows, err = db.Query(query)
  object := map[string]interface{}{}
  columns, _ := rows.Columns()
  values := make([]interface{}, len(columns))

  for rows.Next() {
    err = rows.Scan(values...)
    for i, column := range columns {
      object[column] = new(*string)
      values[i] = object[column]
    }
    data, _ := json.Marshal(object)
    fmt.Fprintf(w,"%s\n", data)
  }

localhost:8080/getuser 的结果:

{“DATE”:null,“QTY”:null,“ID”:null,“USER_NAME”:null}
{“DATE”:null,“QTY”:null,“ID”:null,“USER_NAME”:null}

现在是什么问题?

MapScan 将单行数据扫描到目标映射 map[string]interface{} 中(参考文档),这是在 Go 中打印映射的标准方式。

您可以使用 json.MarshalIndent 进行美化输出,例如:

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	m := map[string]interface{}{"USER_ID": "JD", "USER_NAME": "John Doe"}
	fmt.Println(m)
	b, err := json.MarshalIndent(m, "", "  ")
	if err != nil {
		fmt.Println("error:", err)
	}
	fmt.Print(string(b))
}

在线演示

最终会得到类似这样的输出:

{
  "USER_ID": "JD",
  "USER_NAME": "John Doe"
}

可以使用encoding/json包将map转换为JSON格式。以下是修改后的代码:

rows, err := db.Queryx(query)
for rows.Next() {
    results := make(map[string]interface{})
    err = rows.MapScan(results)
    if err != nil {
        // 处理错误
        continue
    }
    
    jsonData, err := json.Marshal(results)
    if err != nil {
        // 处理JSON序列化错误
        continue
    }
    
    fmt.Fprintf(w, "%s\n", jsonData)
}

输出结果:

{"USER_ID":"JD","USER_NAME":"John Doe"}
{"USER_ID":"JAD","USER_NAME":"Jane Doe"}
{"USER_ID":"DD","USER_NAME":"Donald Duck"}

如果需要美化输出(带缩进),可以使用json.MarshalIndent

jsonData, err := json.MarshalIndent(results, "", "  ")
if err != nil {
    // 处理错误
    continue
}
fmt.Fprintf(w, "%s\n", jsonData)

输出结果:

{
  "USER_ID": "JD",
  "USER_NAME": "John Doe"
}
{
  "USER_ID": "JAD",
  "USER_NAME": "Jane Doe"
}
{
  "USER_ID": "DD",
  "USER_NAME": "Donald Duck"
}
回到顶部