首页 > 文章列表 > Golang实现优雅的将struct转换为map

Golang实现优雅的将struct转换为map

golang
360 2023-02-25

前言

在项目实践中,有时候我们需要将struct结构体转为map映射表,然后基于map做数据裁剪或操作。那么下面我来介绍下常用的两种转换方式,以及对它们做对比,最后采用更优雅的方式,封装到我们的项目工程的工具包里

方式1:使用JSON序列和反序列化

使用json操作的这个方式是比较简单的,容易想到也易实现。直接上代码:

package main

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

type Person struct {
    Name    string `json:"name"`
    Address string `json:"address"`
}

func main() {
    t := time.Now().UnixNano()
    m := make(map[string]interface{})
    person := Person{
       Name:    "zhangsan",
       Address: "北京海淀",
    }
    j, _ := json.Marshal(person)
    json.Unmarshal(j, &m)
    fmt.Println(m)
    fmt.Println(fmt.Sprintf("JSON-duration:%d", time.Now().UnixNano() - t))
}

输出结果:

map[address:北京海淀 name:zhangsan]

JSON-duration:174000

方式2:使用反射

通过反射机制,灵活的做类型转换。具体实现:

package main

import (
    "fmt"
    "reflect"
    "time"
)

type Person struct {
    Name    string `json:"name"`
    Address string `json:"address"`
}

func main() {
    t := time.Now().UnixNano()
    m := make(map[string]interface{})
    person := Person{
        Name:    "zhangsan",
        Address: "北京海淀",
    }

    elem := reflect.ValueOf(&person).Elem()
    relType := elem.Type()
    for i := 0; i < relType.NumField(); i++ {
        name := relType.Field(i).Name
        m[name] = elem.Field(i).Interface()
    }

    fmt.Println(m)
    fmt.Println(fmt.Sprintf("反射-duration:%d", time.Now().UnixNano() - t))
}

输出结果:

map[Address:北京海淀 Name:zhangsan]

反射-duration:60000

两种方式对比

执行效率:

使用反射的效率,明显比使用json的效率要高,接近3倍

输出结果:

使用json能达到预期,正常解析出结构体tag;

使用反射未能达到预期,未解析出结构体tag,字段是以结构体定义为准

封装到工具包

基于上面两种方式的对比,我们决定采用反射机制,并对结构体tag解析做兼容,优雅的将struct转换为map,并封装到工具包中

具体实现,工具包代码:

package utils

import (
    "reflect"
    "strings"
)

type IStruct interface {
    GetStructData() interface{}
}

//struct转map
//使用反射实现,完美地兼容了json标签的处理
func StructToMap(st IStruct) map[string]interface{} {
    m := make(map[string]interface{})
    in := st.GetStructData()
    val := reflect.ValueOf(in)
    if val.Kind() == reflect.Ptr {
        val = val.Elem()
    }
    if val.Kind() != reflect.Struct {
        return m
    }

    relType := val.Type()
    for i := 0; i < relType.NumField(); i++ {
        name := relType.Field(i).Name
        tag := relType.Field(i).Tag.Get("json")
        if tag != "" {
            index := strings.Index(tag, ",")
            if index == -1 {
                name = tag
            } else {
                name = tag[:index]
            }
        }
        m[name] = val.Field(i).Interface()
    }
    return m
}

测试代码:

package main

import (
    "fmt"
    "learn-go/utils"
    "time"
)

type Person struct {
    Name    string `json:"name"`
    Address string `json:"address"`
}

//注意:必须实现这个方法,才能正确调用工具包转换方法
func (p Person) GetStructData() interface{} {
    return p
}

func main() {
    t := time.Now().UnixNano()
    m := make(map[string]interface{})
    person := Person{
        Name:    "zhangsan",
        Address: "北京海淀",
    }

    m = utils.StructToMap(person)
    fmt.Println(m)
    fmt.Println(fmt.Sprintf("反射2-duration:%d", time.Now().UnixNano() - t))
}

输出结果:

map[address:北京海淀 name:zhangsan]

反射2-duration:65000

结论:

执行效率高的同时,输出结果也符合预期,能正确的解析出结构体tag

到此这篇关于Golang实现优雅的将struct转换为map的文章就介绍到这了,更多相关Golang struct转map内容请搜索程序之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序之家!