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

 

前言

在项目实践中,有时候我们需要将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内容请搜索编程宝库以前的文章,希望以后支持编程宝库

 前言viper是适用于go应用程序的配置解决方案,这款配置管理神器,支持多种类型、开箱即用、极易上手。本地配置文件的接入能很快速的完成,那么对于远程apollo配置中心的接入,是 ...