使用Golang将MySQL表转储为JSON

时间:2013-11-15 00:55:31

标签: mysql go

将Go的快速转储程序放到Go中的JSON中。但是我发现从数据库中检索的所有内容都是[]byte数组。因此,我将所有内容编码为字符串,而不是原生的JSON整数或布尔值。

代码的子集:

import (
    "encoding/json"
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)


func dumpTable(w io.Writer, table) {
    // ...

    rows, err := Query(db, fmt.Sprintf("SELECT * FROM %s", table))
    checkError(err)
    columns, err := rows.Columns()
    checkError(err)

    scanArgs := make([]interface{}, len(columns))
    values   := make([]interface{}, len(columns))

    for i := range values {
        scanArgs[i] = &values[i]
    }

    for rows.Next() {
        err = rows.Scan(scanArgs...)
        checkError(err)

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

        for i, col := range values {
            if col != nil {
                fmt.Printf("\n%s: type= %s\n", columns[i], reflect.TypeOf(col))

                switch t := col.(type) {
                default:
                    fmt.Printf("Unexpected type %T\n", t)
                case bool:
                    fmt.Printf("bool\n")
                    record[columns[i]] = col.(bool)
                case int:
                    fmt.Printf("int\n")
                    record[columns[i]] = col.(int)
                case int64:
                    fmt.Printf("int64\n")
                    record[columns[i]] = col.(int64)
                case float64:
                    fmt.Printf("float64\n")
                    record[columns[i]] = col.(float64)
                case string:
                    fmt.Printf("string\n")
                    record[columns[i]] = col.(string)
                case []byte:   // -- all cases go HERE!
                    fmt.Printf("[]byte\n")
                    record[columns[i]] = string(col.([]byte))
                case time.Time:
                    // record[columns[i]] = col.(string)
                }
            }
        }

        s, _ := json.Marshal(record)
        w.Write(s)
        io.WriteString(w, "\n")
    }
}

6 个答案:

答案 0 :(得分:33)

我还需要将数据库表转储到json,这是我实现的方式: (与本主题中的另一个答案不同,一切都不是字符串,感谢这个答案:https://stackoverflow.com/a/17885636/4124416,我可以正确获取整数字段)

func getJSON(sqlString string) (string, error) {
  rows, err := db.Query(sqlString)
  if err != nil {
      return "", err
  }
  defer rows.Close()
  columns, err := rows.Columns()
  if err != nil {
      return "", err
  }
  count := len(columns)
  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)
  }
  jsonData, err := json.Marshal(tableData)
  if err != nil {
      return "", err
  }
  fmt.Println(string(jsonData))
  return string(jsonData), nil 
}

以下是示例输出: [{&#34; ID&#34;:0,&#34;文本&#34;:&#34;零&#34;},{&#34; ID&#34;:1,&#34;文本&# 34;:&#34;一个&#34;},{&#34; ID&#34;:2&#34;文本&#34;:&#34;两个&#34;}]

答案 1 :(得分:3)

需要使用预准备语句来获取本机类型。 MySQL有两个协议,一个作为文本传输,另一个作为&#34; real&#34;类型。并且只有在使用预准备语句时才使用该二进制协议。见https://github.com/go-sql-driver/mysql/issues/407

下面的函数getJSON是正确的:

func getJSON(sqlString string) (string, error) {
  stmt, err := db.Prepare(sqlString)
  if err != nil {
    return "", err
  }
  defer stmt.Close()

  rows, err := stmt.Query()
  if err != nil {
    return "", err
  }
  defer rows.Close()

  columns, err := rows.Columns()
  if err != nil {
    return "", err
  }

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

  count := len(columns)
  values := make([]interface{}, count)
  scanArgs := make([]interface{}, count)
  for i := range values {
    scanArgs[i] = &values[i]
  }

  for rows.Next() {
    err := rows.Scan(scanArgs...)
    if err != nil {
      return "", err
    }

    entry := make(map[string]interface{})
    for i, col := range columns {
      v := values[i]

      b, ok := v.([]byte)
      if (ok) {
        entry[col] = string(b)
      } else {
        entry[col] = v
      }
    }

    tableData = append(tableData, entry)
  }

  jsonData, err := json.Marshal(tableData)
  if err != nil {
    return "", err
  }

  return string(jsonData), nil 
}

答案 2 :(得分:2)

您无能为力,因为driver - database/sql互动几乎是一条单行道,当数据移交给{{1时,驱动程序无法帮助您}}

你可以试试http://godoc.org/github.com/arnehormann/sqlinternals/mysqlinternals

的运气
  • 查询数据库
  • 使用database/sql
  • 检索列切片
  • 创建新的var cols, err := mysqlinternals.Columns(rows)并迭代values := make([]interface{}, len(cols))
  • 使用cols
  • 获取每列最匹配的Go类型
  • 使用refType, err := cols[i].ReflectGoType()
  • 创建类型占位符
  • values[i] = reflect.Zero(refType).Interface()rows.Next()。不要重新创建err = rows.Scan(values...),复制并重复使用它。

我想这仍然会很慢,但是你应该可以随身携带它。 如果您遇到问题,请提出问题 - 我会尽快处理。

答案 3 :(得分:0)

我在practice_db数据库中有一个名为users的表。我在下面的程序中提到了包含数据的表结构,它将users表转换为JSON格式。

您还可以在https://gist.github.com/hygull/1725442b0f121a5fc17b28e04796714d查看源代码。

define('WP_MEMORY_LIMIT', '64M'); 
define('WP_ALLOW_REPAIR', true); 

答案 4 :(得分:0)

根据这里的答案,这是我能想到的最有效的代码。请注意,这会将每一行作为一个单独的JSON数组输出,以保存键名重复。

// OutputJSONMysqlRowsStream outputs rows as a JSON array stream to save ram & output size due to key name repetition
func OutputJSONMysqlRowsStream(writer http.ResponseWriter, rows *sql.Rows) {

    defer rows.Close()

    columns, err := rows.Columns()

    if err != nil {
        OutputJSONError(writer, "Failed to get column names")
        return
    }

    jsonColumns, err := json.Marshal(columns)

    if err != nil {
        OutputJSONError(writer, "Failed to encode json of column names")
        return
    }

    writer.Header().Set("Content-Type", "application/cal-json-stream; charset=utf-8")

    fmt.Fprintln(writer, "{\"status\": \"done\", \"data\":{ \"json_stream_fields\":"+string(jsonColumns)+"}}")

    columnCount := len(columns)
    rowDataHolder := make([]interface{}, columnCount)
    rowDataHolderPointers := make([]interface{}, columnCount)

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

    for rows.Next() {

        for i := 0; i < columnCount; i++ {
            rowDataHolderPointers[i] = &rowDataHolder[i]
        }

        err := rows.Scan(rowDataHolderPointers...)

        if err != nil {
            log.Println(err)
        } else {

            for i, value := range rowDataHolder {
                tempValue, ok := value.([]byte)

                if ok {
                    rowDataHolder[i] = string(tempValue)
                }
            }

            jsonEncoder := json.NewEncoder(writer)
            err = jsonEncoder.Encode(rowDataHolder)

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

答案 5 :(得分:-1)

你可以将表转储到json中,但是一切都将是字符串:(

q := "select * from table"
debug("SQL: %s", q)

rows, err := db.Query(q)
checkError(err)
defer rows.Close()

columns, err := rows.Columns()
checkError(err)

scanArgs := make([]interface{}, len(columns))
values := make([]interface{}, len(columns))

for i := range values {
    scanArgs[i] = &values[i]
}

for rows.Next() {
    err = rows.Scan(scanArgs...)
    checkError(err)

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

    for i, col := range values {
        if col != nil {
            record[columns[i]] = fmt.Sprintf("%s", string(col.([]byte)))
        }
    }

    s, _ := json.Marshal(record)
    fmt.Printf("%s\n", s)
}