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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM