Go语言如何从MySQL获取字段类型正确的JSON数据
问题说明
Golang查询MySQL数据生成JSON时,所有字段值均被序列化为字符串类型,无法保留整数、时间等字段的原始正确类型。
原有问题代码如下:
var emptyOut []byte rows, err := db.Query(sqlString) if err != nil { return emptyOut, err } defer rows.Close() columns, err := rows.Columns() if err != nil { return emptyOut, 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 emptyOut, err } return jsonData, nil
定位到问题根源为以下代码逻辑将所有扫描到的字节切片统一转为了字符串,丢失了原始类型信息:
v = string(b)
解决方法
核心原因是database/sql标准库默认扫描结果时,会将所有变长类型值以[]byte形式返回,需要结合字段的数据库类型做对应转换,有两种成熟实现方案:
方案1:通过列类型信息手动解析
Go 1.8及以上版本支持通过rows.ColumnTypes()获取每一列的数据库类型,根据类型标识将[]byte转为对应的Go原生类型即可,改造后的核心逻辑:
// 先获取所有列的类型元信息 columnTypes, err := rows.ColumnTypes() if err != nil { return emptyOut, err } 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 { val := values[i] b, ok := val.([]byte) // 非字节切片类型直接赋值 if !ok { entry[col] = val continue } // 按数据库字段类型做转换 switch columnTypes[i].DatabaseTypeName() { case "TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT": if intVal, err := strconv.ParseInt(string(b), 10, 64); err == nil { entry[col] = intVal continue } case "FLOAT", "DOUBLE", "DECIMAL": if floatVal, err := strconv.ParseFloat(string(b), 64); err == nil { entry[col] = floatVal continue } case "DATE", "TIME", "DATETIME", "TIMESTAMP": // 按实际存储的时间格式调整layout参数 if timeVal, err := time.Parse("2006-01-02 15:04:05", string(b)); err == nil { entry[col] = timeVal continue } case "JSON": var jsonObj interface{} if err := json.Unmarshal(b, &jsonObj); err == nil { entry[col] = jsonObj continue } default: // VARCHAR/CHAR/TEXT等字符串类型直接转字符串 entry[col] = string(b) } } tableData = append(tableData, entry) }
注意:不同驱动、不同MySQL版本返回的类型名可能存在差异,比如无符号整型会返回INT UNSIGNED,可以本地调试打印实际类型值后调整分支匹配规则。
方案2:驱动自动类型转换
如果使用go-sql-driver/mysql驱动,只需要在数据库连接DSN中添加parseTime=true参数,驱动会自动完成时间、数值等类型的转换,不需要手写解析逻辑。
DSN示例:
root:123456@tcp(127.0.0.1:3306)/test_db?parseTime=true&charset=utf8mb4
如果业务表结构固定,优先定义和表字段一一对应的结构体,扫描时直接传入结构体字段指针,类型准确性和扫描性能都比动态map[string]interface{}方案更好。
内容的提问来源于stack exchange,提问作者Tu Pham
相关产品推荐
相关产品推荐

