GO学习-(38)Go语⾔结构体转map[string]interface{}的若
⼲⽅法
结构体转map[string]interface{}的若⼲⽅法
本⽂介绍了Go语⾔中将结构体转成map[string]interface{}时你需要了解的“坑”,也有你需要知道的若⼲⽅法。
我们在Go语⾔中通常使⽤结构体来保存我们的数据,例如要存储⽤户信息,我们可能会定义如下结构体:
// UserInfo ⽤户信息
type UserInfo struct {
Name string `json:"name"`
Age int `json:"age"`
}
u1 := UserInfo{Name: "q1mi", Age: 18}
假设现在要将上⾯的u1转换成map[string]interface{},该如何操作呢?
结构体转map[string]interface{}
JSON序列化⽅式
这不是很简单吗?我⽤JSON序列化⼀下u1,再反序列化成map不就可以了么。说⼲就⼲,代码如下:
func main() {
u1 := UserInfo{Name: "q1mi", Age: 18}
b, _ := json.Marshal(&u1)
var m map[string]interface{}
_ = json.Unmarshal(b, &m)
for k, v := range m{
fmt.Printf("key:%v value:%v\n", k, v)
}
}
输出:
key:name value:q1mi
key:age value:18
看起来没什么问题,但其实这⾥是有⼀个“坑”的。那就是Go语⾔中的json包在序列化空接⼝存放的数字类型(整型、浮点型等)都会序列化成float64类型。
也就是上⾯例⼦中m["age"]现在底层是⼀个float64了,不是个int了。我们来验证下:
func main() {
u1 := UserInfo{Name: "q1mi", Age: 18}
b, _ := json.Marshal(&u1)
var m map[string]interface{}
_ = json.Unmarshal(b, &m)
for k, v := range m{
fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
}
}
输出:
key:name value:q1mi value type:string
key:age value:18 value type:float64
很显然,出现了⼀个意料之外的⾏为,我们需要想办法规避掉。
反射
没办法就需要⾃⼰动⼿去实现了。这⾥使⽤反射遍历结构体字段的⽅式⽣成map,具体代码如下:
// ToMap 结构体转为Map[string]interface{}
func ToMap(in interface{}, tagName string) (map[string]interface{}, error){
out := make(map[string]interface{})
v := reflect.ValueOf(in)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct { // ⾮结构体返回错误提⽰
return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)
}
t := v.Type()
// 遍历结构体字段
// 指定tagName值为map中key;字段值为map中value
for i := 0; i < v.NumField(); i++ {
fi := t.Field(i)
if tagValue := fi.Tag.Get(tagName); tagValue != "" {
out[tagValue] = v.Field(i).Interface()
}
}
return out, nil
}
验证⼀下:
m2, _ := ToMap(&u1, "json")
for k, v := range m2{
fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
}
输出:
key:name value:q1mi value type:string
key:age value:18 value type:int
这⼀次map["age"]的类型就对了的。
第三⽅库structs
除了⾃⼰实现,Github上也有现成的轮⼦,例如第三⽅库:。
它使⽤的⾃定义结构体tag是structs:
// UserInfo ⽤户信息
type UserInfo struct {
Name string `json:"name" structs:"name"`
Age int `json:"age" structs:"age"`
}
⽤法很简单:
m3 := structs.Map(&u1)
for k, v := range m3 {
fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
}
structs这个包也有很多其他的使⽤⽰例,⼤家可以去查看⽂档。但是需要注意的是⽬前这个库已经被作者设置为只读了。
嵌套结构体转map[string]interface{}
structs本⾝是⽀持嵌套结构体转map[string]interface{}的,遇到结构体嵌套它会转换为map[string]interface{}嵌套map[string]interface{}的模式。
我们定义⼀组嵌套的结构体如下:
// UserInfo ⽤户信息
type UserInfo struct {
Name string `json:"name" structs:"name"`
Age int `json:"age" structs:"age"`
Profile `json:"profile" structs:"profile"`
}
// Profile 配置信息
type Profile struct {
Hobby string `json:"hobby" structs:"hobby"`
}
声明结构体变量u1:
u1 := UserInfo{Name: "q1mi", Age: 18, Profile: Profile{"双⾊球"}}
第三⽅库structs
代码和上⾯的其实是⼀样的:
m3 := structs.Map(&u1)
for k, v := range m3 {
fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
}
输出结果:
key:name value:q1mi value type:string
key:age value:18 value type:int
key:profile value:map[hobby:双⾊球] value type:map[string]interface {}
从结果来看最后嵌套字段profile是map[string]interface {},属于map嵌套map。使⽤反射转成单层map
如果我们想把嵌套的结构体转换成⼀个单层map该怎么做呢?
我们把上⾯反射的代码稍微修改⼀下就可以了:
// ToMap2 将结构体转为单层map
func ToMap2(in interface{}, tag string) (map[string]interface{}, error) {
/
/ 当前函数只接收struct类型
v := reflect.ValueOf(in)
if v.Kind() == reflect.Ptr { // 结构体指针
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v) }
out := make(map[string]interface{})
go语言能做什么queue := make([]interface{}, 0, 1)
queue = append(queue, in)
for len(queue) > 0 {
v := reflect.ValueOf(queue[0])
if v.Kind() == reflect.Ptr { // 结构体指针
v = v.Elem()
}
queue = queue[1:]
t := v.Type()
for i := 0; i < v.NumField(); i++ {
vi := v.Field(i)
if vi.Kind() == reflect.Ptr { // 内嵌指针
vi = vi.Elem()
if vi.Kind() == reflect.Struct { // 结构体
queue = append(queue, vi.Interface())
} else {
ti := t.Field(i)
if tagValue := ti.Tag.Get(tag); tagValue != "" {
// 存⼊map
out[tagValue] = vi.Interface()
}
}
break
}
if vi.Kind() == reflect.Struct { // 内嵌结构体
queue = append(queue, vi.Interface())
break
}
// ⼀般字段
ti := t.Field(i)
if tagValue := ti.Tag.Get(tag); tagValue != "" {
// 存⼊map
out[tagValue] = vi.Interface()
}
}
}
return out, nil
}
测试⼀下:
m4, _ := ToMap2(&u1, "json")
for k, v := range m4 {
fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
}
输出:
key:name value:q1mi value type:string
key:age value:18 value type:int
key:hobby value:双⾊球 value type:string
这下我们就把嵌套的结构体转为单层的map了,但是要注意这种场景下结构体和嵌套结构体的字段就需要避免重复。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论