Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合

定义结构体

结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下

  1. type struct_variable_type struct {
  2. member definition
  3. member definition
  4. ...
  5. member definition
  6. }

一旦定义了结构体类型,它就能用于变量的声明,语法格式如下

  1. variable_name := structure_variable_type {value1, value2...valuen}
  2. variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}

结构体表示一项记录,比如保存图书馆的书籍记录,每本书有以下属性:

  • ID 书籍ID
  • Title 标题
  • Author 作者
  • Subject 学科 ```go package main import “fmt”

type Books struct { title string author string subject string book_id int }

func main() { // 创建一个新的结构体 fmt.Println(Books{“Go 语言”, “www.runoob.com”, “Go 语言教程”, 6495407})

// 也可以使用 key => value 格式 fmt.Println(Books{title: “Go 语言”, author: “www.runoob.com”, subject: “Go 语言教程”, book_id: 6495407})

// 忽略的字段为 0 或 空 fmt.Println(Books{title: “Go 语言”, author: “www.runoob.com”}) }

/ 输出结果为: {Go 语言 www.runoob.com Go 语言教程 6495407} {Go 语言 www.runoob.com Go 语言教程 6495407} {Go 语言 www.runoob.com 0} /

  1. <a name="NhOMl"></a>
  2. ### 访问结构体成员
  3. 如果要访问结构体成员,需要使用点号 **.** 操作符,格式为
  4. ```go
  5. 结构体.成员名"

结构体类型变量使用 struct 关键字定义,实例如下:

  1. package main
  2. import "fmt"
  3. type Books struct {
  4. title string
  5. author string
  6. subject string
  7. book_id int
  8. }
  9. func main() {
  10. var Book1 Books // 声明 Book1 为 Books 类型
  11. var Book2 Books // 声明 Book2 为 Books 类型
  12. // book 1 描述
  13. Book1.title = "Go 语言"
  14. Book1.author = "www.runoob.com"
  15. Book1.subject = "Go 语言教程"
  16. Book1.book_id = 6495407
  17. // book 2 描述
  18. Book2.title = "Python 教程"
  19. Book2.author = "www.runoob.com"
  20. Book2.subject = "Python 语言教程"
  21. Book2.book_id = 6495700
  22. // 打印 Book1 信息
  23. fmt.Printf( "Book 1 title : %s\n", Book1.title)
  24. fmt.Printf( "Book 1 author : %s\n", Book1.author)
  25. fmt.Printf( "Book 1 subject : %s\n", Book1.subject)
  26. fmt.Printf( "Book 1 book_id : %d\n", Book1.book_id)
  27. // 打印 Book2 信息
  28. fmt.Printf( "Book 2 title : %s\n", Book2.title)
  29. fmt.Printf( "Book 2 author : %s\n", Book2.author)
  30. fmt.Printf( "Book 2 subject : %s\n", Book2.subject)
  31. fmt.Printf( "Book 2 book_id : %d\n", Book2.book_id)
  32. }
  33. /*
  34. 以上实例执行运行结果为:
  35. Book 1 title : Go 语言
  36. Book 1 author : www.runoob.com
  37. Book 1 subject : Go 语言教程
  38. Book 1 book_id : 6495407
  39. Book 2 title : Python 教程
  40. Book 2 author : www.runoob.com
  41. Book 2 subject : Python 语言教程
  42. Book 2 book_id : 6495700
  43. */

结构体作为函数参数

你可以像其他数据类型一样将结构体类型作为参数传递给函数。并以以上实例的方式访问结构体变量:

  1. package main
  2. import "fmt"
  3. type Books struct {
  4. title string
  5. author string
  6. subject string
  7. book_id int
  8. }
  9. func main() {
  10. var Book1 Books // 声明 Book1 为 Books 类型
  11. var Book2 Books // 声明 Book2 为 Books 类型
  12. // book 1 描述
  13. Book1.title = "Go 语言"
  14. Book1.author = "www.runoob.com"
  15. Book1.subject = "Go 语言教程"
  16. Book1.book_id = 6495407
  17. // book 2 描述
  18. Book2.title = "Python 教程"
  19. Book2.author = "www.runoob.com"
  20. Book2.subject = "Python 语言教程"
  21. Book2.book_id = 6495700
  22. printBook(Book1) // 打印 Book1 信息
  23. printBook(Book2) // 打印 Book2 信息
  24. }
  25. func printBook( book Books ) {
  26. fmt.Printf( "Book title : %s\n", book.title)
  27. fmt.Printf( "Book author : %s\n", book.author)
  28. fmt.Printf( "Book subject : %s\n", book.subject)
  29. fmt.Printf( "Book book_id : %d\n", book.book_id)
  30. }
  31. /*
  32. Book title : Go 语言
  33. Book author : www.runoob.com
  34. Book subject : Go 语言教程
  35. Book book_id : 6495407
  36. Book title : Python 教程
  37. Book author : www.runoob.com
  38. Book subject : Python 语言教程
  39. Book book_id : 6495700
  40. */

结构体指针

你可以定义指向结构体的指针类似于其他指针变量,格式如下:

  1. var struct_pointer *Books

以上定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 & 符号放置于结构体变量前:

  1. struct_pointer = &Book1

使用结构体指针访问结构体成员,使用 “.” 操作符:

  1. struct_pointer.title

接下来让我们使用结构体指针重写以上实例,代码如下:

  1. package main
  2. import "fmt"
  3. type Books struct {
  4. title string
  5. author string
  6. subject string
  7. book_id int
  8. }
  9. func main() {
  10. var Book1 Books // Declare Book1 of type Book
  11. var Book2 Books // Declare Book2 of type Book
  12. // book 1 描述
  13. Book1.title = "Go 语言"
  14. Book1.author = "www.runoob.com"
  15. Book1.subject = "Go 语言教程"
  16. Book1.book_id = 6495407
  17. // book 2 描述
  18. Book2.title = "Python 教程"
  19. Book2.author = "www.runoob.com"
  20. Book2.subject = "Python 语言教程"
  21. Book2.book_id = 6495700
  22. printBook(&Book1) // 打印 Book1 信息
  23. printBook(&Book2) // 打印 Book2 信息
  24. }
  25. func printBook( book *Books ) {
  26. fmt.Printf( "Book title : %s\n", book.title)
  27. fmt.Printf( "Book author : %s\n", book.author)
  28. fmt.Printf( "Book subject : %s\n", book.subject)
  29. fmt.Printf( "Book book_id : %d\n", book.book_id)
  30. }
  31. /*
  32. Book title : Go 语言
  33. Book author : www.runoob.com
  34. Book subject : Go 语言教程
  35. Book book_id : 6495407
  36. Book title : Python 教程
  37. Book author : www.runoob.com
  38. Book subject : Python 语言教程
  39. Book book_id : 6495700
  40. */

结构体是作为参数的值传递

  1. package main
  2. import "fmt"
  3. type Books struct {
  4. title string
  5. author string
  6. subject string
  7. book_id int
  8. }
  9. func changeBook(book Books,title string) {
  10. book.title = title
  11. }
  12. func main() {
  13. var book1 Books
  14. book1.title = "book1"
  15. book1.author = "zuozhe"
  16. book1.book_id = 1
  17. changeBook(book1,"title被修改了")
  18. fmt.Println(book1)
  19. }
  20. /*
  21. 期盼结果:
  22. {title被修改了 zuozhe 1}
  23. 实际结果为:
  24. {book1 zuozhe 1}
  25. */

如果想在函数里面改变结果体数据内容,需要传入指针:

  1. package main
  2. import "fmt"
  3. type Books struct {
  4. title string
  5. author string
  6. subject string
  7. book_id int
  8. }
  9. func changeBook(book *Books,title string) {
  10. book.title = title
  11. }
  12. func main() {
  13. var book1 Books
  14. book1.title = "book1"
  15. book1.author = "zuozhe"
  16. book1.book_id = 1
  17. changeBook(&book1,"title被修改了")
  18. fmt.Println(book1)
  19. }
  20. /*
  21. 运行结果:
  22. {title被修改了 zuozhe 1}
  23. */

struct 中定义成员变量

struct 类似于 java 中的类,但是不需要通过 getter, setter 来设置访问权限。要访问成员变量,可以:

  • 通过 struct变量.成员变量来访问、重新赋值
  • 通过 struct指针.成员变量来访问、重新赋值 ```go type Rect struct{ //定义矩形类 x,y float64 //类型只包含属性,并没有方法 width,height float64 }

func (r Rect) getArea() float64{ //为Rect类型绑定Area的方法,Rect为指针引用可以修改传入参数的值 return r.width*r.height //方法归属于类型,不归属于具体的对象,声明该类型的对象即可调用该类型的方法 }

//_ package main import ( “fmt” )

type Rect struct{
x,y float64
width,height int }

func (r Rect) getArea() int{ return r.widthr.height }

func main() { var rectExample Rect rectExample.x = 10 rectExample.width = 20 rectExample.height = 30 rectExample.x = 40 fmt.Println(rectExample.getArea()) }

  1. <a name="HjgZM"></a>
  2. ### Golang自定义结构体转map
  3. 在Golang中,如何将一个结构体转成map? 本文介绍两种方法。第一种是是使用json包解析解码编码。第二种是使用反射,使用反射的效率比较高
  4. <a name="fCPr5"></a>
  5. #### json包的marshal,unmarshal
  6. ```go
  7. package main
  8. import (
  9. "fmt"
  10. "time"
  11. "encoding/json"
  12. // "reflect"
  13. // "strconv"
  14. // "strings"
  15. // "testing"
  16. )
  17. const timeLayout = "2006-01-02 15:04:05"
  18. type User struct {
  19. Name string `map:"name,omitempty"` // string
  20. Github GithubPage `map:"github,dive,omitempty"` // struct dive
  21. NoDive StructNoDive `map:"no_dive,omitempty"` // no dive struct
  22. MyProfile Profile `map:"my_profile,omitempty"` // struct implements its own method
  23. }
  24. type GithubPage struct {
  25. URL string `map:"url"`
  26. Star int `map:"star"`
  27. }
  28. type StructNoDive struct {
  29. NoDive int
  30. }
  31. type Profile struct {
  32. Experience string `map:"experience"`
  33. Date time.Time `map:"time"`
  34. }
  35. // its own toMap method
  36. func (p Profile) StructToMap() (key string, value interface{}) {
  37. return "time", p.Date.Format(timeLayout)
  38. }
  39. func newUser() User {
  40. name := "user"
  41. MyGithub := GithubPage{
  42. URL: "https://github.com/liangyaopei/structmap",
  43. Star: 1,
  44. }
  45. NoDive := StructNoDive{NoDive: 1}
  46. dateStr := "2020-07-21 12:00:00"
  47. date, _ := time.Parse(timeLayout, dateStr)
  48. profile := Profile{
  49. Experience: "my experience",
  50. Date: date,
  51. }
  52. return User{
  53. Name: name,
  54. Github: MyGithub,
  55. NoDive: NoDive,
  56. MyProfile: profile,
  57. }
  58. }
  59. func main() {
  60. user := newUser()
  61. data, _ := json.Marshal(&user)
  62. m := make(map[string]interface{})
  63. json.Unmarshal(data, &m)
  64. fmt.Println(m)
  65. }

先将结构体序列化成 [] byte 数组,再从 [] byte 数组序列化成结构体

  1. data, _ := json.Marshal(&user)
  2. m := make(map[string]interface{})
  3. json.Unmarshal(data, &m)
  • 使用简单 劣势
  • 效率比较慢
  • 不能支持一些定制的键,也不能支持一些定制的方法,例如将struct的域展开等

使用反射

本文实现了使用反射将结构体转成 map 的方法。通过标签(tag)和反射,将上文示例的 newUser() 返回的结果转化成下面的一个map。其中包含struct的域的展开,定制化struct的方法
1.标签识别
使用readTag方法读取域(field)的标签,如果没有标签,使用域的名字。然后读取tag中的选项。目前支持3个选项

  • ‘-‘:忽略当前这个域
  • omitempty : 当这个域的值为空,忽略这个域
  • dive : 递归地遍历这个结构体,将所有字段作为键

如果选中了一个选项,就讲这个域对应的二进制位置为1

  1. const (
  2. OptIgnore = "-"
  3. OptOmitempty = "omitempty"
  4. OptDive = "dive"
  5. )
  6. const (
  7. flagIgnore = 1 << iota
  8. flagOmiEmpty
  9. flagDive
  10. )
  11. func readTag(f reflect.StructField, tag string) (string, int) {
  12. val, ok := f.Tag.Lookup(tag)
  13. fieldTag := ""
  14. flag := 0
  15. // no tag, use field name
  16. if !ok {
  17. return f.Name, flag
  18. }
  19. opts := strings.Split(val, ",")
  20. fieldTag = opts[0]
  21. for i := 1; i < len(opts); i++ {
  22. switch opts[i] {
  23. case OptIgnore:
  24. flag |= flagIgnore
  25. case OptOmitempty:
  26. flag |= flagOmiEmpty
  27. case OptDive:
  28. flag |= flagDive
  29. }
  30. }
  31. return fieldTag, flag
  32. }

2.结构体的域(field)的遍历
遍历结构体的每一个域(field),判断field的类型(kind)。如果是string,int等的基本类型,直接取值,并且把标签中的值作为key

  1. for i := 0; i < t.NumField(); i++ {
  2. ...
  3. switch fieldValue.Kind() {
  4. case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64:
  5. res[tagVal] = fieldValue.Int()
  6. case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64:
  7. res[tagVal] = fieldValue.Uint()
  8. case reflect.Float32, reflect.Float64:
  9. res[tagVal] = fieldValue.Float()
  10. case reflect.String:
  11. res[tagVal] = fieldValue.String()
  12. case reflect.Bool:
  13. res[tagVal] = fieldValue.Bool()
  14. default:
  15. }
  16. }
  17. }

3.内嵌结构体的转换
如果是结构体,先检查有没有实现传入参数的方法,如果实现了,就调用这个方法。如果没有实现,就递归地调用StructToMap方法,然后根据是否展开(dive),来把返回结果写入res的map

  1. for i := 0; i < t.NumField(); i++ {
  2. fieldType := t.Field(i)
  3. // ignore unexported field
  4. if fieldType.PkgPath != "" {
  5. continue
  6. }
  7. // read tag
  8. tagVal, flag := readTag(fieldType, tag)
  9. if flag&flagIgnore != 0 {
  10. continue
  11. }
  12. fieldValue := v.Field(i)
  13. if flag&flagOmiEmpty != 0 && fieldValue.IsZero() {
  14. continue
  15. }
  16. // ignore nil pointer in field
  17. if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
  18. continue
  19. }
  20. if fieldValue.Kind() == reflect.Ptr {
  21. fieldValue = fieldValue.Elem()
  22. }
  23. // get kind
  24. switch fieldValue.Kind() {
  25. case reflect.Struct:
  26. _, ok := fieldValue.Type().MethodByName(methodName)
  27. if ok {
  28. key, value, err := callFunc(fieldValue, methodName)
  29. if err != nil {
  30. return nil, err
  31. }
  32. res[key] = value
  33. continue
  34. }
  35. // recursive
  36. deepRes, deepErr := StructToMap(fieldValue.Interface(), tag, methodName)
  37. if deepErr != nil {
  38. return nil, deepErr
  39. }
  40. if flag&flagDive != 0 {
  41. for k, v := range deepRes {
  42. res[k] = v
  43. }
  44. } else {
  45. res[tagVal] = deepRes
  46. }
  47. default:
  48. }
  49. }
  50. ...
  51. }
  52. // call function
  53. func callFunc(fv reflect.Value, methodName string) (string, interface{}, error) {
  54. methodRes := fv.MethodByName(methodName).Call([]reflect.Value{})
  55. if len(methodRes) != methodResNum {
  56. return "", nil, fmt.Errorf("wrong method %s, should have 2 output: (string,interface{})", methodName)
  57. }
  58. if methodRes[0].Kind() != reflect.String {
  59. return "", nil, fmt.Errorf("wrong method %s, first output should be string", methodName)
  60. }
  61. key := methodRes[0].String()
  62. return key, methodRes[1], nil
  63. }

4.array,slice类型的转换
如果是array,slice类型,类似地,检查有没有实现传入参数的方法,如果实现了,就调用这个方法。如果没有实现,将这个field的tag作为key,域的值作为value

  1. switch fieldValue.Kind() {
  2. case reflect.Slice, reflect.Array:
  3. _, ok := fieldValue.Type().MethodByName(methodName)
  4. if ok {
  5. key, value, err := callFunc(fieldValue, methodName)
  6. if err != nil {
  7. return nil, err
  8. }
  9. res[key] = value
  10. continue
  11. }
  12. res[tagVal] = fieldValue
  13. ....
  14. }

5.其他类型
对于其他类型,例如内嵌的map,直接将其返回结果的值

  1. switch fieldValue.Kind() {
  2. ...
  3. case reflect.Map:
  4. res[tagVal] = fieldValue
  5. case reflect.Chan:
  6. res[tagVal] = fieldValue
  7. case reflect.Interface:
  8. res[tagVal] = fieldValue.Interface()
  9. default:
  10. }

完整案例

创建 structmap.go 文件,写入代码

  1. package structmap
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strings"
  6. )
  7. const (
  8. methodResNum = 2
  9. )
  10. const (
  11. OptIgnore = "-"
  12. OptOmitempty = "omitempty"
  13. OptDive = "dive"
  14. )
  15. const (
  16. flagIgnore = 1 << iota
  17. flagOmiEmpty
  18. flagDive
  19. )
  20. // StructToMap convert a golang sturct to a map
  21. // key can be specified by tag, LIKE `map:"tag"`.
  22. // If there is no tag, struct filed name will be used instead
  23. // methodName is the name the field has implemented.
  24. // If implemented, it uses the method to get the key and value
  25. func StructToMap(s interface{}, tag string, methodName string) (res map[string]interface{}, err error) {
  26. v := reflect.ValueOf(s)
  27. if v.Kind() == reflect.Ptr && v.IsNil() {
  28. return nil, fmt.Errorf("%s is a nil pointer", v.Kind().String())
  29. }
  30. if v.Kind() == reflect.Ptr {
  31. v = v.Elem()
  32. }
  33. // only accept struct param
  34. if v.Kind() != reflect.Struct {
  35. return nil, fmt.Errorf("s is not a struct but %s", v.Kind().String())
  36. }
  37. t := v.Type()
  38. res = make(map[string]interface{})
  39. for i := 0; i < t.NumField(); i++ {
  40. fieldType := t.Field(i)
  41. // ignore unexported field
  42. if fieldType.PkgPath != "" {
  43. continue
  44. }
  45. // read tag
  46. tagVal, flag := readTag(fieldType, tag)
  47. if flag&flagIgnore != 0 {
  48. continue
  49. }
  50. fieldValue := v.Field(i)
  51. if flag&flagOmiEmpty != 0 && fieldValue.IsZero() {
  52. continue
  53. }
  54. // ignore nil pointer in field
  55. if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
  56. continue
  57. }
  58. if fieldValue.Kind() == reflect.Ptr {
  59. fieldValue = fieldValue.Elem()
  60. }
  61. // get kind
  62. switch fieldValue.Kind() {
  63. case reflect.Slice, reflect.Array:
  64. _, ok := fieldValue.Type().MethodByName(methodName)
  65. if ok {
  66. key, value, err := callFunc(fieldValue, methodName)
  67. if err != nil {
  68. return nil, err
  69. }
  70. res[key] = value
  71. continue
  72. }
  73. res[tagVal] = fieldValue
  74. case reflect.Struct:
  75. _, ok := fieldValue.Type().MethodByName(methodName)
  76. if ok {
  77. key, value, err := callFunc(fieldValue, methodName)
  78. if err != nil {
  79. return nil, err
  80. }
  81. res[key] = value
  82. continue
  83. }
  84. // recursive
  85. deepRes, deepErr := StructToMap(fieldValue.Interface(), tag, methodName)
  86. if deepErr != nil {
  87. return nil, deepErr
  88. }
  89. if flag&flagDive != 0 {
  90. for k, v := range deepRes {
  91. res[k] = v
  92. }
  93. } else {
  94. res[tagVal] = deepRes
  95. }
  96. case reflect.Map:
  97. res[tagVal] = fieldValue
  98. case reflect.Chan:
  99. res[tagVal] = fieldValue
  100. case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64:
  101. res[tagVal] = fieldValue.Int()
  102. case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64:
  103. res[tagVal] = fieldValue.Uint()
  104. case reflect.Float32, reflect.Float64:
  105. res[tagVal] = fieldValue.Float()
  106. case reflect.String:
  107. res[tagVal] = fieldValue.String()
  108. case reflect.Bool:
  109. res[tagVal] = fieldValue.Bool()
  110. case reflect.Complex64, reflect.Complex128:
  111. res[tagVal] = fieldValue.Complex()
  112. case reflect.Interface:
  113. res[tagVal] = fieldValue.Interface()
  114. default:
  115. }
  116. }
  117. return
  118. }
  119. // readTag read tag with format `json:"name,omitempty"` or `json:"-"`
  120. // For now, only supports above format
  121. func readTag(f reflect.StructField, tag string) (string, int) {
  122. val, ok := f.Tag.Lookup(tag)
  123. fieldTag := ""
  124. flag := 0
  125. // no tag, use field name
  126. if !ok {
  127. return f.Name, flag
  128. }
  129. opts := strings.Split(val, ",")
  130. fieldTag = opts[0]
  131. for i := 1; i < len(opts); i++ {
  132. switch opts[i] {
  133. case OptIgnore:
  134. flag |= flagIgnore
  135. case OptOmitempty:
  136. flag |= flagOmiEmpty
  137. case OptDive:
  138. flag |= flagDive
  139. }
  140. }
  141. return fieldTag, flag
  142. }
  143. // call function
  144. func callFunc(fv reflect.Value, methodName string) (string, interface{}, error) {
  145. methodRes := fv.MethodByName(methodName).Call([]reflect.Value{})
  146. if len(methodRes) != methodResNum {
  147. return "", nil, fmt.Errorf("wrong method %s, should have 2 output: (string,interface{})", methodName)
  148. }
  149. if methodRes[0].Kind() != reflect.String {
  150. return "", nil, fmt.Errorf("wrong method %s, first output should be string", methodName)
  151. }
  152. key := methodRes[0].String()
  153. return key, methodRes[1], nil
  154. }

创建 to_map_test.go 文件,写入代码

  1. package struct_to_map_test
  2. import (
  3. "encoding/json"
  4. "reflect"
  5. "strconv"
  6. "strings"
  7. "testing"
  8. "time"
  9. "github.com/liangyaopei/structmap"
  10. )
  11. const timeLayout = "2006-01-02 15:04:05"
  12. type Profile struct {
  13. Experience string `map:"experience"`
  14. Date time.Time `map:"time"`
  15. }
  16. // its own toMap method
  17. func (p Profile) StructToMap() (key string, value interface{}) {
  18. return "time", p.Date.Format(timeLayout)
  19. }
  20. type MySlice []int
  21. // its own toMap method
  22. func (a MySlice) StructToMap() (string, interface{}) {
  23. key := "array"
  24. b := strings.Builder{}
  25. if len(a) == 0 {
  26. return key, b.String()
  27. }
  28. for i := 0; i < len(a); i++ {
  29. b.WriteString(strconv.Itoa(a[i]) + ",")
  30. }
  31. return key, b.String()
  32. }
  33. func TestCallMethod(t *testing.T) {
  34. arr := MySlice{1, 2, 3}
  35. v := reflect.ValueOf(&arr)
  36. typ := v.Type()
  37. t.Logf("type:%v,value:%v", v.Type(), v)
  38. methodName := "StructToMap"
  39. method, ok := typ.MethodByName(methodName)
  40. t.Logf("method:%v,ok:%v", method, ok)
  41. methodRes := v.MethodByName(methodName).Call([]reflect.Value{})
  42. key := methodRes[0].String()
  43. resValue := methodRes[1].String()
  44. t.Logf("key:%s,value:%v", key, resValue)
  45. }
  46. // alias type
  47. type Gender int
  48. const (
  49. male Gender = 1
  50. female Gender = 2
  51. )
  52. // Dive struct
  53. type GithubPage struct {
  54. URL string `map:"url"`
  55. Star int `map:"star"`
  56. }
  57. type StructNoDive struct {
  58. NoDive int
  59. }
  60. // User is used for demonstration
  61. type User struct {
  62. Name string `map:"name,omitempty"` // string
  63. Email *string `map:"email_ptr,omitempty"` // pointer
  64. MyGender Gender `map:"gender,omitempty"` // type alias
  65. Github GithubPage `map:"github,dive,omitempty"` // struct dive
  66. NoDive StructNoDive `map:"no_dive,omitempty"` // no dive struct
  67. MyProfile Profile `map:"my_profile,omitempty"` // struct implements its own method
  68. Arr []int `map:"arr,omitempty"` // normal slice
  69. MyArr MySlice `map:"my_arr,omitempty"` // slice implements its own method
  70. }
  71. func newUser() User {
  72. name := "user"
  73. email := "yaopei.liang@foxmail.com"
  74. myGender := male
  75. MyGithub := GithubPage{
  76. URL: "https://github.com/liangyaopei",
  77. Star: 1,
  78. }
  79. NoDive := StructNoDive{NoDive: 1}
  80. dateStr := "2020-07-21 12:00:00"
  81. date, _ := time.Parse(timeLayout, dateStr)
  82. profile := Profile{
  83. Experience: "my experience",
  84. Date: date,
  85. }
  86. arr := []int{1, 2, 3}
  87. myArr := MySlice{11, 12, 13}
  88. return User{
  89. Name: name,
  90. Email: &email,
  91. MyGender: myGender,
  92. Github: MyGithub,
  93. NoDive: NoDive,
  94. MyProfile: profile,
  95. Arr: arr,
  96. MyArr: myArr,
  97. }
  98. }
  99. func TestStructToMap(t *testing.T) {
  100. user := newUser()
  101. tag := "map"
  102. methodName := "StructToMap"
  103. res, err := structmap.StructToMap(&user, tag, methodName)
  104. if err != nil {
  105. t.Errorf("struct to map:%s", err.Error())
  106. return
  107. }
  108. for k, v := range res {
  109. t.Logf("k:%v,v:%v", k, v)
  110. }
  111. }
  112. type benchmarkUser struct {
  113. Name string `json:"name"`
  114. Age int `json:"age"`
  115. Address string `json:"address"`
  116. Contact string `json:"contact"`
  117. }
  118. func newBenchmarkUser() benchmarkUser {
  119. return benchmarkUser{
  120. Name: "name",
  121. Age: 18,
  122. Address: "github address",
  123. Contact: "github contact",
  124. }
  125. }
  126. func BenchmarkStructToMapByJson(b *testing.B) {
  127. user := newBenchmarkUser()
  128. b.ResetTimer()
  129. for i := 0; i < b.N; i++ {
  130. data, _ := json.Marshal(&user)
  131. m := make(map[string]interface{})
  132. json.Unmarshal(data, &m)
  133. }
  134. }
  135. func BenchmarkStructToMapByToMap(b *testing.B) {
  136. user := newBenchmarkUser()
  137. tag := "json"
  138. methodName := ""
  139. b.ResetTimer()
  140. for i := 0; i < b.N; i++ {
  141. structmap.StructToMap(&user, tag, methodName)
  142. }
  143. }