如何在Go语言中扁平化动态值为键值对切片?
解决Go语言动态值扁平化为键值对切片的问题
你的代码存在几个关键错误,导致结果完全不符合预期:
- 混淆了
reflect.Type和reflect.Value:你用reflect.TypeOf(o)拿到类型后,错误地将这个类型传给reflect.ValueOf(),正确做法是直接对输入的o调用reflect.ValueOf()获取值实例。 - 结构体处理逻辑错误:把每个结构体字段包装成
KeyValuePair后再递归,导致递归处理的是KeyValuePair的key和value字段,而非原字段本身的值。 - 简单类型的key处理错误:直接把值转成字符串作为key,不符合你需要用字段名/变量名作为key的需求。
下面是修正后的实现,支持处理简单类型、结构体、嵌套结构体,还扩展了切片、数组和map的处理:
package main import ( "fmt" "reflect" ) type KeyValuePair struct { Key string Value interface{} } // flattenObject 接收值和当前前缀(用于嵌套结构的key拼接),返回扁平化后的键值对 func flattenObject(o interface{}, prefix string) []KeyValuePair { var result []KeyValuePair v := reflect.ValueOf(o) // 处理指针类型,自动解引用 for v.Kind() == reflect.Ptr { if v.IsNil() { return result } v = v.Elem() } switch v.Kind() { case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.String: if prefix != "" { result = append(result, KeyValuePair{Key: prefix, Value: v.Interface()}) } case reflect.Struct: t := v.Type() for i := 0; i < v.NumField(); i++ { field := v.Field(i) fieldName := t.Field(i).Name // 拼接前缀,生成嵌套结构的key(如D.C) newPrefix := fieldName if prefix != "" { newPrefix = fmt.Sprintf("%s.%s", prefix, fieldName) } // 递归处理字段值 result = append(result, flattenObject(field.Interface(), newPrefix)...) } case reflect.Slice, reflect.Array: for i := 0; i < v.Len(); i++ { elemPrefix := fmt.Sprintf("%s[%d]", prefix, i) result = append(result, flattenObject(v.Index(i).Interface(), elemPrefix)...) } case reflect.Map: iter := v.MapRange() for iter.Next() { key := iter.Key().Interface() keyStr, ok := key.(string) if !ok { keyStr = fmt.Sprintf("%v", key) } mapPrefix := keyStr if prefix != "" { mapPrefix = fmt.Sprintf("%s.%s", prefix, keyStr) } result = append(result, flattenObject(iter.Value().Interface(), mapPrefix)...) } } return result } // Flatten 顶层调用封装,无需传入前缀 func Flatten(o interface{}) []KeyValuePair { return flattenObject(o, "") } func main() { // 测试嵌套结构体 type Inner struct { C bool } type Test struct { A int B string D Inner } test := Test{ A: 10, B: "test", D: Inner{C: true}, } kvs := Flatten(test) for _, kv := range kvs { fmt.Printf("Key: %s, Value: %v\n", kv.Key, kv.Value) } // 输出: // Key: A, Value: 10 // Key: B, Value: test // Key: D.C, Value: true // 测试顶层简单值(需手动传入key作为前缀) simpleKvs := flattenObject(10, "x") fmt.Println(simpleKvs) // 输出:[{x 10}] }
关键修改说明:
- 新增
prefix参数,用于拼接嵌套结构的key,比如嵌套结构体字段生成D.C格式的key - 修复反射逻辑错误,正确获取值实例并处理指针解引用
- 结构体处理时直接递归字段值,而非包装成
KeyValuePair再递归 - 扩展支持切片、数组、map类型的扁平化处理
- 顶层调用封装为
Flatten函数,简化使用
注意:Go的反射无法获取顶层简单值的变量名(比如单独的10),如果需要返回{"x", 10}这类结果,需要手动传入key作为前缀,例如调用flattenObject(10, "x")。
内容的提问来源于stack exchange,提问作者dragonfly02
相关产品推荐
相关产品推荐

