结构体转map
借助反射和标签实现结构体转map
代码
go
func StructToMap(data any, tagName string) (mp map[string]any, err error) {
// 初始化一个空的 map 用于存储结果
mp = make(map[string]any)
// 使用 reflect 包获取传入数据的反射值
v := reflect.ValueOf(data)
// 检查传入的数据是否为结构体类型
if v.Kind() != reflect.Struct {
// 如果不是结构体,则返回错误
err = errors.New("传入转化的对象非结构体")
return
}
// 打印结构体类型信息,可用于调试
fmt.Println(v.Type())
// 遍历结构体的每个字段
for i := 0; i < v.NumField(); i++ {
// 获取当前字段的反射值
val := v.Field(i)
// 获取当前字段的类型信息,并从指定标签中取得 tag 值
tag := v.Type().Field(i).Tag.Get(tagName)
// 如果 tag 为空或者等于 "-", 则跳过此字段
if tag == "" || tag == "-" {
continue
}
// 特别处理指针类型的字段
if val.Kind() == reflect.Ptr {
// 如果指针是 nil, 跳过此字段
if val.IsNil() {
continue
}
// 否则,解引用指针并将其值存入 map 中
mp[tag] = val.Elem().Interface()
continue
}
// 对于非指针类型的字段,直接将字段值存入 map 中
mp[tag] = val.Interface()
}
// 返回生成的 map 和 nil 错误表示成功
return
}
工作流程
- 初始化结果容器 :
- 创建一个空的
map[string]any
来存储转换后的键值对。
- 创建一个空的
- 获取传入参数的反射值 :
- 使用
reflect.ValueOf(data)
获取传入结构体的反射值v
。
- 使用
- 验证输入是否为结构体 :
- 检查
v.Kind()
是否等于reflect.Struct
。如果不是,则抛出错误并终止函数执行。
- 检查
- 遍历结构体字段 :
- 使用
v.NumField()
方法得到结构体字段的数量,并使用 for 循环迭代这些字段。
- 使用
- 处理每个字段 :
- 对于每一个字段,获取其反射值
val
和对应的类型信息。 - 使用
v.Type().Field(i).Tag.Get(tagName)
获取指定标签名的 tag 值,这通常用于序列化配置(如 JSON, XML 标签)。 - 如果 tag 是空字符串或者特定标记
-
,则跳过该字段,不加入到最终的 map 中。
- 对于每一个字段,获取其反射值
- 处理指针类型字段 :
- 当字段是
reflect.Ptr
类型时,额外检查它是否为 nil。如果是非 nil 的指针,解引用指针以获取实际值,并将该值存入 map 中。
- 当字段是
- 添加字段到 map :
- 将字段值(直接值或指针指向的值)和对应的 tag 名称作为键值对添加到 map 中。
- 返回结果 :
- 最后,函数返回填充好的 map 和任何可能发生的错误。
测试
go
func TestStructToMap(t *testing.T) {
type successTestStruct struct {
String string `tag-string:"string"`
Int int `tag-number:"int"`
Bool bool `tag-bool:"bool"`
}
var test = successTestStruct{
String: "测试",
Int: 10086,
Bool: false,
}
var testMap map[string]any
var err error
if testMap, err = struct_to_map.StructToMap(test, "tag-string"); testMap["string"] != "测试" {
fmt.Println(err)
t.Fatal("字符串结构体转map失败")
} else {
fmt.Println(testMap)
t.Log("字符串结构体转map成功")
}
if testMap, err = struct_to_map.StructToMap(test, "tag-number"); testMap["int"] != 10086 {
fmt.Println(err)
t.Fatal("整型结构体转map失败")
} else {
fmt.Println(testMap)
t.Log("整型结构体转map成功")
}
if testMap, err = struct_to_map.StructToMap(test, "tag-bool"); testMap["bool"] != false {
fmt.Println(err)
t.Fatal("布尔结构体转map失败")
} else {
fmt.Println(testMap)
t.Log("布尔结构体转map成功")
}
}
测试结果
bash
=== RUN TestStructToMap
map[string:测试]
StructToMap_test.go:28: 字符串结构体转map成功
map[int:10086]
StructToMap_test.go:35: 整型结构体转map成功
map[bool:false]
StructToMap_test.go:42: 布尔结构体转map成功
--- PASS: TestStructToMap (0.00s)
PASS