如何在Golang中动态映射MS-SQL表并生成JSON?
解决Go从MS-SQL生成含动态Z_前缀字段的JSON问题
嘿,这个动态字段的问题确实挺头疼的,毕竟Go是静态类型语言,对付这种结构不固定的场景得动点小脑筋。结合你提到的情况——既有固定结构体字段,又有随非Go系统动态生成的Z_前缀字段,还有辅助表的存在,我给你梳理几个可行的方案:
核心思路:用动态Map替代静态结构体
既然结构体没法适配动态变化的字段,咱们就换个思路,用map[string]interface{}来统一存储固定字段和动态字段,最后直接序列化这个Map成JSON就行。
步骤1:从辅助表获取当前表的动态字段列表
假设你的辅助表(比如叫dynamic_fields)里存了每条动态字段的信息:比如所属表名、字段名、数据类型(比如int/varchar)。先查询这个表拿到当前Demotable对应的所有Z_开头的字段:
// 先定义辅助表对应的结构体 type DynamicField struct { TableName string FieldName string DataType string } func getDynamicFields(db *sql.DB) ([]DynamicField, error) { rows, err := db.Query("SELECT table_name, field_name, data_type FROM dynamic_fields WHERE table_name = 'Demotable' AND field_name LIKE 'Z_%'") if err != nil { return nil, err } defer rows.Close() var fields []DynamicField for rows.Next() { var f DynamicField if err := rows.Scan(&f.TableName, &f.FieldName, &f.DataType); err != nil { return nil, err } fields = append(fields, f) } return fields, rows.Err() }
步骤2:查询主表并构建动态Map
拿到动态字段列表后,咱们可以拼接SQL查询语句,把固定字段和动态字段都查出来,然后遍历每一行数据,把固定字段和动态字段都塞进Map里:
func queryDemotableAndGenerateJSON(db *sql.DB) ([]byte, error) { // 1. 获取动态字段 dynamicFields, err := getDynamicFields(db) if err != nil { return nil, err } // 2. 拼接查询字段:固定字段 + 动态字段 fixedFields := []string{"Id", "Name", "Adress"} allFields := append(fixedFields, func() []string { var fs []string for _, f := range dynamicFields { fs = append(fs, f.FieldName) } return fs }()...) // 3. 构建SQL语句 sqlStr := fmt.Sprintf("SELECT %s FROM Demotable", strings.Join(allFields, ", ")) rows, err := db.Query(sqlStr) if err != nil { return nil, err } defer rows.Close() // 4. 获取所有列名(用于Map的key) cols, err := rows.Columns() if err != nil { return nil, err } // 5. 遍历每一行数据,构建Map var results []map[string]interface{} for rows.Next() { // 准备存储每一行数据的切片,用interface{}接收 values := make([]interface{}, len(cols)) valuePtrs := make([]interface{}, len(cols)) for i := range values { valuePtrs[i] = &values[i] } if err := rows.Scan(valuePtrs...); err != nil { return nil, err } // 构建当前行的Map rowMap := make(map[string]interface{}) for i, col := range cols { // 根据辅助表的类型,转换数据类型(可选,如果你需要严格类型的话) val := values[i] for _, df := range dynamicFields { if df.FieldName == col { switch df.DataType { case "int": // 处理MS-SQL的int类型,比如sql.NullInt64转int if ni, ok := val.(sql.NullInt64); ok && ni.Valid { val = int(ni.Int64) } else { val = nil // 或者你想要的默认值 } case "varchar", "nvarchar": // 处理字符串类型,比如sql.NullString if ns, ok := val.(sql.NullString); ok && ns.Valid { val = ns.String } else { val = "" } } break } } rowMap[col] = val } results = append(results, rowMap) } // 6. 序列化成JSON return json.MarshalIndent(results, "", " ") }
步骤3:直接序列化Map到JSON
上面的函数最后返回的就是JSON字节数组,你可以直接输出或者保存。这样不管Z_前缀的字段怎么变,只要辅助表能正确记录,代码就能自动适配。
额外小贴士
- 如果辅助表没有存储数据类型,也可以通过
rows.ColumnTypes()获取字段的类型信息,然后做类型转换,不过这个方法对MS-SQL的兼容性需要测试。 - 如果你还是想保留结构体的静态类型(比如固定字段的类型安全),可以先把固定字段扫描到结构体,再把动态字段塞进Map,最后把结构体转成Map和动态字段Map合并,再序列化:
// 示例:合并结构体和动态字段Map demo := Demotable{} // 扫描固定字段到demo... demoMap, _ := json.Marshal(demo) var fixedMap map[string]interface{} json.Unmarshal(demoMap, &fixedMap) // 合并fixedMap和动态字段的rowMap for k, v := range rowMap { fixedMap[k] = v } // 再序列化fixedMap成JSON
这样既能享受固定字段的类型安全,又能兼容动态字段的变化。
内容的提问来源于stack exchange,提问作者pitw
相关产品推荐
相关产品推荐

