JSON将数字视为base64 golang

Trying to send query output to browser as JSON, but numbers are treated as Base64. Integers prints out correct.

 var rows *sqlx.Rows 
 enc := json.NewEncoder(w)

 rows, err = db.Queryx(query)
  for rows.Next() {
    results := make(map[string]interface{})
    err = rows.MapScan(results)
    if err := enc.Encode(results); err != nil{
       fmt.Fprintf(w,"%s
", results)
    }
 }

The result from JSON is (id integer, qty numeric / float):

{"ID":1,"QTY":"OC4wMA=="}

{"ID":2,"QTY":"OC4wMA=="}

Without JSON encoding, the numeric column is treated correct as numbers.

EDIT

@mkopriva hope this answer helps:

"if you provide the table definition so we can see how the column is defined, it would also help to see the SQL query so we can see how you're pulling the column from the db"

"ID"  int4
"QTY" numeric 

SELECT * FROM table

and it would also help if you provide the code and its output which you mentioned as "treating the columns correctly as numbers"

var rows *sqlx.Rows 

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

Gives this result:

map[ID:1 QTY:9.75]

map[ID:2 QTY:7.00]

"can you do fmt.Printf("%T", results["QTY"]) for us? It is highly doubtful that if "QTY" is truly an int or float that the json encoder would marshal it as a base64 string.

without JSON this gives:

[]uint8 []uint8

After countless attempts, I found a solution that solves the problem by converting numbers to strings. Not ideal, but rather simple.

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

for i, _ := range cols {  //bytes to string
   vals[i] = &cols[i]
}

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

for i, v := range vals {  //adding column names
  mymap[colnames[i]] = v
}

for rows.Next() {
   err = rows.Scan(vals...)
   json, _ := json.Marshal(mymap)
   fmt.Fprintf(w,"%s
",json)
}

Feel free to downvote, but please do me the honour to explain why.

Assuming you're using lib/pq, the QTY value is being converted into a byte slice which is actually the correct type for the corresponding postgres numeric type. This is because a value of type numeric cannot be reliably fit into a float64 without loosing precision and so it makes sense for the driver to choose to convert a postgres numeric value into a Go []byte value.

See here: https://github.com/lib/pq/issues/648#issuecomment-322674708

You can, however, explicitly tell the driver that you need a float by passing a Go value of type *float64 to the Scan family of methods and the driver will convert the numeric into a float64, losing precission in the process. But if you pass an interface{}, the driver will make the most appropriate choice and convert the value to a byte slice.

If you don't care about precision but you must to use interface{} as the arguemnt to Scan, you can change the table column's type from numeric to float, if you do that then the driver can safely make the choice to convert your column into a Go float64.

If you care about precision, but for some reason you have to use a map of interfaces, you can write a simple function that converts a known key's value in the map from a []byte to a string, after that, when you pass the updated map to the json encoder you'll see that key's value being encoded as a normal string instead of base64.

func bytesToString(k string, m map[string]interface{}) {
    if b, ok := m[k].([]byte); ok {
        m[k] = string(b)
    }
}

https://play.golang.org/p/Ekih0oLd4-L