web-dev-qa-db-fra.com

Transfert de tables MySQL vers JSON avec Golang

Était en train de monter un dumper rapide pour MySQL à JSON dans Go. Cependant, je trouve que tout ce que je récupère de la base de données est un tableau []byte. Ainsi, au lieu d’entiers ou de booléens JSON natifs, tout est encodé sous forme de chaîne.

Sous-ensemble du code:

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")
    }
}
16
koblas

Je devais aussi vider les tables de la base de données sur json et voici comment j'ai réalisé: (Différent d'une autre réponse dans cette rubrique, tout n'est pas chaîne, grâce à cette réponse: https://stackoverflow.com/ a/17885636/4124416 , Je pourrais obtenir les champs entiers correctement) 

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 
}

Voici un exemple de sortie: [{"ID": 0, "Texte": "Zéro"}, {"ID": 1, "Texte": "Un"}, {"ID": 2 , "Texte": "Deux"}]

31
turkenh

Il est nécessaire d'utiliser des instructions préparées pour obtenir les types natifs. MySQL a deux protocoles, l’un transmettant le texte, l’autre, le type "réel". Et ce protocole binaire n'est utilisé que lorsque vous utilisez des instructions préparées. Voir https://github.com/go-sql-driver/mysql/issues/407

La fonction getJSON ci-dessous est correcte:

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 
}
3
Vadim Homchik

Vous ne pouvez pas faire grand chose, car l’interaction driver - database/sql est à peu près une rue à sens unique et le conducteur ne peut vous aider avec quoi que ce soit lorsque les données sont transmises à database/sql.

Vous pouvez tenter votre chance avec http://godoc.org/github.com/arnehormann/sqlinternals/mysqlinternals

  • Interroger la base de données
  • Récupérer la tranche de colonne avec cols, err := mysqlinternals.Columns(rows)
  • Créer une nouvelle var values := make([]interface{}, len(cols)) et effectuer une itération sur cols
  • Obtenez le type de correspondance le plus proche par colonne avec refType, err := cols[i].ReflectGoType()
  • Créer des espaces réservés de type avec values[i] = reflect.Zero(refType).Interface()
  • rows.Next() et err = rows.Scan(values...). Ne recréez pas values, copiez-le et réutilisez-le.

Je suppose que cela sera encore assez lent, mais vous devriez pouvoir aller quelque part avec cela. Si vous rencontrez des problèmes, veuillez classer un problème - je le ferai dès que possible.

2
Arne

J'ai une table nommée utilisateurs dans la base de données practice_db. J'ai mentionné la structure de table avec des données dans le programme suivant qui convertit la table des utilisateurs au format JSON. 

Vous pouvez également consulter le code source à l’adresse https://Gist.github.com/hygull/1725442b0f121a5fc17b28e04796714d

/**
    {
        "created_on": "26 may 2017",
        "todos": [
            "go get github.com/go-sql-driver/mysql",
            "postman(optional)",
            "browser(optional)",    
        ],
        "aim": "Reading fname column into []string(slice of strings)"
    }
*/


/* 
    mysql> select * from users;
    +----+-----------+----------+----------+-------------------------------+--------------+-------------------------------------------------------------------------------------------------+
    | id | fname     | lname    | uname    | email                         | contact      | profile_pic                                                                                     |
    +----+-----------+----------+----------+-------------------------------+--------------+-------------------------------------------------------------------------------------------------+
    |  1 | Rishikesh | Agrawani | hygull   | [email protected] | 917353787704 | https://cdn4.iconfinder.com/data/icons/rcons-user/32/user_group_users_accounts_contacts-512.png |
    |  2 | Sandeep   | E        | sandeep  | [email protected]       | 919739040038 | https://cdn4.iconfinder.com/data/icons/eldorado-user/40/user-512.png                            |
    |  3 | Darshan   | Sidar    | darshan  | [email protected]        | 917996917565 | https://cdn4.iconfinder.com/data/icons/rcons-user/32/child_boy-512.png                          |
    |  4 | Surendra  | Prajapat | surendra | [email protected]     | 918385894407 | https://cdn4.iconfinder.com/data/icons/rcons-user/32/account_male-512.png                       |
    |  5 | Mukesh    | Jakhar   | mukesh   | [email protected]     | 919772254140 | https://cdn2.iconfinder.com/data/icons/rcons-user/32/male-circle-512.png                        |
    +----+-----------+----------+----------+-------------------------------+--------------+-------------------------------------------------------------------------------------------------+
    5 rows in set (0.00 sec)

    mysql> 
*/

package main

import "log"
import "net/http"
import "encoding/json"

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

func users(w http.ResponseWriter, r *http.Request) {
    // db, err := sql.Open("mysql", "<username>:<password>@tcp(127.0.0.1:<port>)/<dbname>?charset=utf8" )
    db, err := sql.Open("mysql", "hygull:admin@67@tcp(127.0.0.1:3306)/practice_db?charset=utf8")

    w.Header().Set("Content-Type", "application/json")

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

    rows, err := db.Query("select id, fname, lname, uname, email, contact, profile_pic from users")

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

    type User struct {
        Id int       `json:"id"`
        Fname string `json:"firstname"`
        Lname string `json:"lastname"`
        Uname string `json:"username"`
        Email string `json:"email"`
        Contact int `json:"contact"`
        ProfilePic string `json:"profile_pic"`
    }

    var users []User

    for rows.Next() {
        var id, contact int
        var fname string
        var lname string
        var uname, email, profile_pic string

        rows.Scan(&id ,&fname, &lname, &uname, &email, &contact, &profile_pic)
        users = append(users, User{id, fname, lname, uname, email, contact, &profile_pic })
    }

    usersBytes, _ := json.Marshal(&users)

    w.Write(usersBytes)
    db.Close()
}

func main() {
    http.HandleFunc("/users/", users)
    http.ListenAndServe(":8080", nil)
}

/* REQUSET

   http://127.0.0.1:8080/users/
*/

/* RESPONSE
[
  {
    "id": 1,
    "firstname": "Rishikesh",
    "lastname": "Agrawani",
    "username": "hygull",
    "email": "[email protected]",
    "contact": 917353787704,
    "profile_pic": "https://cdn4.iconfinder.com/data/icons/rcons-user/32/user_group_users_accounts_contacts-512.png"
  },
  {
    "id": 2,
    "firstname": "Sandeep",
    "lastname": "E",
    "username": "sandeep",
    "email": "[email protected]",
    "contact": 919739040038,
    "profile_pic": "https://cdn4.iconfinder.com/data/icons/eldorado-user/40/user-512.png"
  },
  {
    "id": 3,
    "firstname": "Darshan",
    "lastname": "Sidar",
    "username": "darshan",
    "email": "[email protected]",
    "contact": 917996917565,
    "profile_pic": "https://cdn4.iconfinder.com/data/icons/rcons-user/32/child_boy-512.png"
  },
  {
    "id": 4,
    "firstname": "Surendra",
    "lastname": "Prajapat",
    "username": "surendra",
    "email": "[email protected]",
    "contact": 918385894407,
    "profile_pic": "https://cdn4.iconfinder.com/data/icons/rcons-user/32/account_male-512.png"
  },
  {
    "id": 5,
    "firstname": "Mukesh",
    "lastname": "Jakhar",
    "username": "mukesh",
    "email": "[email protected]",
    "contact": 919772254140,
    "profile_pic": "https://cdn2.iconfinder.com/data/icons/rcons-user/32/male-circle-512.png"
  }
]
*/
0
hygull