当前位置:   article > 正文

结构体转map[string]interface{}的方法_make(map[string]interface{})

make(map[string]interface{})

以下内容转载自 https://blog.csdn.net/changqi008/article/details/105471250

结构体转map[string]interface{}

  • 在go语言中结构体转map[stirng]interface{}中有几种方法,但是也有一些小小的"坑",比如说我们在存储一些信息时有各种类型的,下面来看一个结构体:
    1. // UserInfo 用户信息
    2. type UserInfo struct {
    3. Name string `json:"name"`
    4. Age int `json:"age"`
    5. }
    6. u1 := UserInfo{Name: "奇奇", Age: 18}
    将此结构体转成map[string]interface{}
  1. JSON反序列化

    1. func main() {
    2. u1 := UserInfo{Name: "奇奇", Age: 18}
    3. b, _ := json.Marshal(&u1)
    4. var m map[string]interface{}
    5. _ = json.Unmarshal(b, &m)
    6. for k, v := range m{
    7. fmt.Printf("key:%v value:%v\n", k, v)
    8. }
    9. }

    看到的结果是:

    1. key:name value:奇奇
    2. key:age value:18

    看起来好像没有什么问题,但是,当我们看他们的类型的时候你就会发现有不一样的地方了
    我们来验证下

    1. func main() {
    2. u1 := UserInfo{Name: "奇奇", Age: 18}
    3. b, _ := json.Marshal(&u1)
    4. var m map[string]interface{}
    5. _ = json.Unmarshal(b, &m)
    6. for k, v := range m{
    7. fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
    8. }
    9. }

    再来看下结果

    1. key:name value:奇奇 value type:string
    2. key:age value:18 value type:float64

    可以看出age的类型明明是int型的,转成map之后就变成了float64类型了呢?
    原因是:

    在JSON协议中没有整型和浮点型之分,它们统称为Number,json字符串中的数组经过go语言中的json经过反序列化之后都会成为float64,但是我们也有办法在转成int型,就是先要将得到的float64经过序列化之后得到json.number类型之后,在转为int类型.

  2. 反射
    除了上面说到的方法之外,我们还有利用反射遍历结构体的方式生成map

    1. // ToMap 结构体转为Map[string]interface{}
    2. func ToMap(in interface{}, tagName string) (map[string]interface{}, error){
    3. out := make(map[string]interface{})
    4. v := reflect.ValueOf(in)
    5. if v.Kind() == reflect.Ptr {
    6. v = v.Elem()
    7. }
    8. if v.Kind() != reflect.Struct { // 非结构体返回错误提示
    9. return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)
    10. }
    11. t := v.Type()
    12. // 遍历结构体字段
    13. // 指定tagName值为map中key;字段值为map中value
    14. for i := 0; i < v.NumField(); i++ {
    15. fi := t.Field(i)
    16. if tagValue := fi.Tag.Get(tagName); tagValue != "" {
    17. out[tagValue] = v.Field(i).Interface()
    18. }
    19. }
    20. return out, nil
    21. }

    对此可以验证下

    1. m2, _ := ToMap(&u1, "json")
    2. for k, v := range m2{
    3. fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
    4. }

    可以看到输出的结果为:

    1. key:name value:奇奇 value type:string
    2. key:age value:18 value type:int
    • 这个看到的就没有问题了,但是,用反射的方式有一点不好就是比较耗性能,因为利用反射的时候不知道你的类型,所以需要一个个去匹配,多以相对来说,性能没有那么好.
  3. 第三方库structs
    除了上述自己实现的以外,在github上面也有一些实现好了的轮子,比如:https://github.com/fatih/structs
    它使用的是自定义结构体:struts

    1. // UserInfo 用户信息
    2. type UserInfo struct {
    3. Name string `json:"name" structs:"name"`
    4. Age int `json:"age" structs:"age"`
    5. }

    用法是:

    1. m3 := structs.Map(&u1)
    2. for k, v := range m3 {
    3. fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
    4. }

    是的,是不是方便很多,在这个包中还有其他方法,可以查看文档,据说被作者设置成只读模式

嵌套结构体转map[string]interface{}

  1. structs本身是支持嵌套结构体转map[string]interface{}的,遇到结构体嵌套它会转换为map[string]interface{}嵌套map[string]interface{}的模式。
    先来定义一个嵌套的结构体:
    1. // UserInfo 用户信息
    2. type UserInfo struct {
    3. Name string `json:"name" structs:"name"`
    4. Age int `json:"age" structs:"age"`
    5. Profile `json:"profile" structs:"profile"`
    6. }
    7. // Profile 配置信息
    8. type Profile struct {
    9. Hobby string `json:"hobby" structs:"hobby"`
    10. }
    申明结构体变了u1
    u1 := UserInfo{Name: "奇奇", Age: 18, Profile: Profile{"双色球"}}
    
    •  
    利用第三方库structs
    1. m3 := structs.Map(&u1)
    2. for k, v := range m3 {
    3. fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
    4. }
    输出结果:
    1. key:name value:奇奇 value type:string
    2. key:age value:18 value type:int
    3. key:profile value:map[hobby:双色球] value type:map[string]interface {}
    从结果来看最后嵌套字段profile是map[string]interface {},属于map嵌套map。
  2. 利用反射转成单层map
    利用反射如何将嵌套的map转成单层的map呢
    1. // ToMap2 将结构体转为单层map
    2. func ToMap2(in interface{}, tag string) (map[string]interface{}, error) {
    3. // 当前函数只接收struct类型
    4. v := reflect.ValueOf(in)
    5. if v.Kind() == reflect.Ptr { // 结构体指针
    6. v = v.Elem()
    7. }
    8. if v.Kind() != reflect.Struct {
    9. return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)
    10. }
    11. out := make(map[string]interface{})
    12. queue := make([]interface{}, 0, 1)
    13. queue = append(queue, in)
    14. for len(queue) > 0 {
    15. v := reflect.ValueOf(queue[0])
    16. if v.Kind() == reflect.Ptr { // 结构体指针
    17. v = v.Elem()
    18. }
    19. queue = queue[1:]
    20. t := v.Type()
    21. for i := 0; i < v.NumField(); i++ {
    22. vi := v.Field(i)
    23. if vi.Kind() == reflect.Ptr { // 内嵌指针
    24. vi = vi.Elem()
    25. if vi.Kind() == reflect.Struct { // 结构体
    26. queue = append(queue, vi.Interface())
    27. } else {
    28. ti := t.Field(i)
    29. if tagValue := ti.Tag.Get(tag); tagValue != "" {
    30. // 存入map
    31. out[tagValue] = vi.Interface()
    32. }
    33. }
    34. break
    35. }
    36. if vi.Kind() == reflect.Struct { // 内嵌结构体
    37. queue = append(queue, vi.Interface())
    38. break
    39. }
    40. // 一般字段
    41. ti := t.Field(i)
    42. if tagValue := ti.Tag.Get(tag); tagValue != "" {
    43. // 存入map
    44. out[tagValue] = vi.Interface()
    45. }
    46. }
    47. }
    48. return out, nil
    49. }
    测试一下:
    1. m4, _ := ToMap2(&u1, "json")
    2. for k, v := range m4 {
    3. fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
    4. }
    输出:
    1. key:name value:奇奇 value type:string
    2. key:age value:18 value type:int
    3. key:hobby value:双色球 value type:string
    这下我们就把嵌套的结构体转为单层的map了,但是要注意这种场景下结构体和嵌套结构体的字段就需要避免重复。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/907899
推荐阅读
相关标签
  

闽ICP备14008679号