go语言结构体转map的方法

使用json序列化与反序列化的方式(有一个数字转换的坑)

package t8

import (
    "encoding/json"
    "fmt"
    "testing"
)

type Student struct{
    Name string `json:"name"`
    Age int `json:"age"`
}

// JSON序列化方式
func jsonStructToMap(stuObj *Student) (map[string]interface{}, error){
    // 结构体转json
    strRet, err := json.Marshal(stuObj)
    if err != nil{
        return nil, err
    }
    // json转map
    var mRet map[string]interface{}
    err1 := json.Unmarshal(strRet, &mRet)
    if err1 != nil{
        return nil, err1
    }
    return mRet, nil
}


func TestJsonMethod(t *testing.T) {
    // 测试 struct使用json直接转map
    stuObj1 := Student{
        Name: "whw",
        Age: 28,
    }
    mRet, err := jsonStructToMap(&stuObj1)
    if err != nil{
        panic(fmt.Sprintf("json方式出错: %s \n", err.Error()))
    }
    // 查看一下转换后的结果
    for key, val := range mRet{
        fmt.Printf("key: %v, value: %v, typeValue: %T \n", key, val, val)
    }
    /*
        key: name, value: whw, typeValue: string
        key: age, value: 28, typeValue: float64  ——————————  注意这里age变成了 float64!!!
    */
}

使用反射将单层的struct转换为map

package t8

import (
    "fmt"
    "reflect"
    "testing"
)

//type Student struct{
//    Name string `json:"name"`
//    Age int `json:"age"`
//}

// ToMap 结构体转为Map[string]interface{}
func StructToMapReflect(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
}

func TestReflectMethod(t *testing.T) {
    stuObj := Student{
        Name: "wanghw",
        Age: 22,
    }

    m2, _ := StructToMapReflect(&stuObj,"json")
    for key, val := range m2{
        fmt.Printf("key: %s, val: %v, typeVal: %T \n", key, val, val)
    }
    /*
        key: name, val: wanghw, typeVal: string
        key: age, val: 22, typeVal: int       ——————  这里正常了,age还是int类型
    */
}

使用structs包转换单层的struct为map

首先需要下载structs包

go get github.com/fatih/structs

代码

package t8


import (
    "fmt"
    "github.com/fatih/structs"
    "testing"
)

type Student struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func TestStructsMethod(t *testing.T) {

    stuObj := Student{
        Name: "naruto",
        Age:  23,
    }

    m3 := structs.Map(&stuObj)
    for key, val := range m3 {
        fmt.Printf("key: %s, val: %v, typeVal: %T \n", key, val, val)
    }

    // key: Name, val: naruto, typeVal: string
    // key: Age, val: 23, typeVal: int

}

使用structs包或反射的方法转换嵌套的结构体为map

package t8

import (
    "fmt"
    "github.com/fatih/structs"
    "reflect"
    "testing"
)

// 嵌套结构体转
// 用户信息
type UserInfo struct {
    Name    string  `json:"name" structs:"name"`
    Age     int     `json:"age" structs:"age"`
    Profile Profile `json:"profile" structs:"profile"`
}

// 配置信息
type Profile struct {
    Hobby string `json:"hobby" structs:"hobby"`
}

// 声明一个全局user
var userObj = UserInfo{
    Name: "whw",
    Age:  18,
    Profile: Profile{
        Hobby: "computer",
    },
}

// TODO 1、structs转换方法
func TestStructsMethod(t *testing.T) {
    m3 := structs.Map(&userObj)
    for k, v := range m3 {
        fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
    }
    /*
        key:name value:whw value type:string
        key:age value:18 value type:int
        key:profile value:map[hobby:computer] value type:map[string]interface {}
    */
}

// TODO 2、使用反射转成单层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{})
    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
}

func TestStructToSingleMap(t *testing.T) {
    m4, _ := ToMap2(&userObj, "json")
    for k, v := range m4 {
        fmt.Printf("key:%v value:%v value type:%T\n", k, v, v)
    }
    /*
        key:name value:whw value type:string
        key:age value:18 value type:int
        key:hobby value:computer value type:string
    */
}

参考博客

https://www.liwenzhou.com/posts/Go/struct2map/