Struct转换

项目中我们经常会遇到大量struct的使用,以及各种数据类型到struct的转换/赋值(特别是json/xml/各种协议编码转换的时候)。为提高编码及项目维护效率,gconv模块为各位开发者带来了极大的福利,为数据解析提供了更高的灵活度。

gconv模块执行struct转换的方法仅有两个,定义如下:

  1. func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) error
  2. func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error

其中:

  1. params为需要转换到struct的变量参数,可以为任意数据类型,常见的数据类型为map
  2. pointer为需要执行转的目标struct对象,这个参数必须为该struct的对象指针,转换成功后该对象的属性将会更新;
  3. mapping为自定义的map键名strcut属性之间的映射关系,此时params参数必须为map类型,否则该参数无意义;
  4. StructDeep相比较于Struct方法,区别是支持递归转换,即会同时递归转换其属性中的结构体对象,特别用于转换带有继承结构的自定义struct,详见后续示例;

转换规则

gconv模块的struct转换特性非常强大,支持任意数据类型到struct属性的映射转换。在没有提供自定义mapping转换规则的情况下,默认的转换规则如下:

  1. struct中需要匹配的属性必须为公开属性(首字母大写);
  2. 根据params类型的不同,逻辑会有不同:
    • params参数为map: 键名会自动按照 不区分大小写忽略-/_/空格符号 的形式与struct属性进行匹配;
    • params参数为其他类型: 将会把该变量值与struct的第一个属性进行匹配;
    • 此外,如果struct的属性为复杂数据类型如slice,map,strcut那么会进行递归匹配赋值;
  3. 如果匹配成功,那么将键值赋值给属性,如果无法匹配,那么忽略该键值;

以下是几个匹配的示例:

  1. map键名 struct属性 是否匹配
  2. name Name match
  3. Email Email match
  4. nickname NickName match
  5. NICKNAME NickName match
  6. Nick-Name NickName match
  7. nick_name NickName match
  8. nick name NickName match
  9. NickName Nick_Name match
  10. Nick-name Nick_Name match
  11. nick_name Nick_Name match
  12. nick name Nick_Name match

自动初始化

当给定的pointer参数类型为**struct时,Struct方法内部将会自动进行初始化创建对象,并修改传递变量指向的指针地址。这种自动对象初始化的方式,对于Golang的GC来说比较友好,防止开发者创建过多无意义的临时对象。

  1. package main
  2. import (
  3. "github.com/gogf/gf/frame/g"
  4. "github.com/gogf/gf/util/gconv"
  5. )
  6. func main() {
  7. type User struct {
  8. Uid int
  9. Name string
  10. }
  11. user := (*User)(nil)
  12. params := g.Map{
  13. "uid": 1,
  14. "name": "john",
  15. }
  16. err := gconv.Struct(params, &user)
  17. if err != nil {
  18. panic(err)
  19. }
  20. g.Dump(user)
  21. }

执行后,输出结果为:

  1. {
  2. "Name": "john",
  3. "Uid": 1
  4. }

Struct递归转换

递归转换是指当struct对象包含子对象时,可以将params参数数据(第一个参数)同时递归地映射到其子对象上,常用于带有继承对象的struct上。

可以使用StructDeep方法实现递归转换。

  1. package main
  2. import (
  3. "github.com/gogf/gf/frame/g"
  4. "github.com/gogf/gf/util/gconv"
  5. )
  6. func main() {
  7. type Ids struct {
  8. Id int `json:"id"`
  9. Uid int `json:"uid"`
  10. }
  11. type Base struct {
  12. Ids
  13. CreateTime string `json:"create_time"`
  14. }
  15. type User struct {
  16. Base
  17. Passport string `json:"passport"`
  18. Password string `json:"password"`
  19. Nickname string `json:"nickname"`
  20. }
  21. data := g.Map{
  22. "id" : 1,
  23. "uid" : 100,
  24. "passport" : "john",
  25. "password" : "123456",
  26. "nickname" : "John",
  27. "create_time" : "2019",
  28. }
  29. user := new(User)
  30. gconv.StructDeep(data, user)
  31. g.Dump(user)
  32. }

执行后,终端输出结果为:

  1. {
  2. "Base": {
  3. "id": 1,
  4. "uid": 100,
  5. "create_time": "2019"
  6. },
  7. "nickname": "John",
  8. "passport": "john",
  9. "password": "123456"
  10. }

示例1,基本使用

  1. package main
  2. import (
  3. "github.com/gogf/gf/frame/g"
  4. "github.com/gogf/gf/util/gconv"
  5. )
  6. type User struct {
  7. Uid int
  8. Name string
  9. Site_Url string
  10. NickName string
  11. Pass1 string `gconv:"password1"`
  12. Pass2 string `gconv:"password2"`
  13. }
  14. func main() {
  15. user := (*User)(nil)
  16. // 使用默认映射规则绑定属性值到对象
  17. user = new(User)
  18. params1 := g.Map{
  19. "uid" : 1,
  20. "Name" : "john",
  21. "siteurl" : "https://goframe.org",
  22. "nick_name" : "johng",
  23. "PASS1" : "123",
  24. "PASS2" : "456",
  25. }
  26. if err := gconv.Struct(params1, user); err == nil {
  27. g.Dump(user)
  28. }
  29. // 使用struct tag映射绑定属性值到对象
  30. user = new(User)
  31. params2 := g.Map {
  32. "uid" : 2,
  33. "name" : "smith",
  34. "site-url" : "https://goframe.org",
  35. "nick name" : "johng",
  36. "password1" : "111",
  37. "password2" : "222",
  38. }
  39. if err := gconv.Struct(params2, user); err == nil {
  40. g.Dump(user)
  41. }
  42. }

可以看到,我们可以直接通过Struct方法将map按照默认规则绑定到struct上,也可以使用struct tag的方式进行灵活的设置。此外,Struct方法有第三个map参数,用于指定自定义的参数名称到属性名称的映射关系。

执行后,输出结果为:

  1. {
  2. "Uid": 1,
  3. "Name": "john",
  4. "Site_Url": "https://goframe.org",
  5. "NickName": "johng",
  6. "Pass1": "123",
  7. "Pass2": "456"
  8. }
  9. {
  10. "Uid": 2,
  11. "Name": "smith",
  12. "Site_Url": "https://goframe.org",
  13. "NickName": "johng",
  14. "Pass1": "111",
  15. "Pass2": "222"
  16. }

示例2,复杂类型

1. struct属性为struct/*struct

属性支持struct对象或者struct对象指针(目标为指针时,转换时会自动初始化)转换。

  1. package main
  2. import (
  3. "github.com/gogf/gf/util/gconv"
  4. "github.com/gogf/gf/frame/g"
  5. "fmt"
  6. )
  7. func main() {
  8. type Score struct {
  9. Name string
  10. Result int
  11. }
  12. type User1 struct {
  13. Scores Score
  14. }
  15. type User2 struct {
  16. Scores *Score
  17. }
  18. user1 := new(User1)
  19. user2 := new(User2)
  20. scores := g.Map{
  21. "Scores": g.Map{
  22. "Name": "john",
  23. "Result": 100,
  24. },
  25. }
  26. if err := gconv.Struct(scores, user1); err != nil {
  27. fmt.Println(err)
  28. } else {
  29. g.Dump(user1)
  30. }
  31. if err := gconv.Struct(scores, user2); err != nil {
  32. fmt.Println(err)
  33. } else {
  34. g.Dump(user2)
  35. }
  36. }

执行后,输出结果为:

  1. {
  2. "Scores": {
  3. "Name": "john",
  4. "Result": 100
  5. }
  6. }
  7. {
  8. "Scores": {
  9. "Name": "john",
  10. "Result": 100
  11. }
  12. }

2. struct属性为slice,数值为slice

  1. package main
  2. import (
  3. "github.com/gogf/gf/util/gconv"
  4. "github.com/gogf/gf/frame/g"
  5. "fmt"
  6. )
  7. func main() {
  8. type Score struct {
  9. Name string
  10. Result int
  11. }
  12. type User struct {
  13. Scores []Score
  14. }
  15. user := new(User)
  16. scores := g.Map{
  17. "Scores": g.Slice{
  18. g.Map{
  19. "Name": "john",
  20. "Result": 100,
  21. },
  22. g.Map{
  23. "Name": "smith",
  24. "Result": 60,
  25. },
  26. },
  27. }
  28. // 嵌套struct转换,属性为slice类型,数值为slice map类型
  29. if err := gconv.Struct(scores, user); err != nil {
  30. fmt.Println(err)
  31. } else {
  32. g.Dump(user)
  33. }
  34. }

执行后,输出结果为:

  1. {
  2. "Scores": [
  3. {
  4. "Name": "john",
  5. "Result": 100
  6. },
  7. {
  8. "Name": "smith",
  9. "Result": 60
  10. }
  11. ]
  12. }

3. struct属性为slice,数值为非slice

  1. package main
  2. import (
  3. "github.com/gogf/gf/util/gconv"
  4. "github.com/gogf/gf/frame/g"
  5. "fmt"
  6. )
  7. func main() {
  8. type Score struct {
  9. Name string
  10. Result int
  11. }
  12. type User struct {
  13. Scores []Score
  14. }
  15. user := new(User)
  16. scores := g.Map{
  17. "Scores": g.Map{
  18. "Name": "john",
  19. "Result": 100,
  20. },
  21. }
  22. // 嵌套struct转换,属性为slice类型,数值为map类型
  23. if err := gconv.Struct(scores, user); err != nil {
  24. fmt.Println(err)
  25. } else {
  26. g.Dump(user)
  27. }
  28. }

执行后,输出结果为:

  1. {
  2. "Scores": [
  3. {
  4. "Name": "john",
  5. "Result": 100
  6. }
  7. ]
  8. }

4. struct属性为[]*struct

  1. package main
  2. import (
  3. "github.com/gogf/gf/util/gconv"
  4. "github.com/gogf/gf/frame/g"
  5. "fmt"
  6. )
  7. func main() {
  8. type Score struct {
  9. Name string
  10. Result int
  11. }
  12. type User struct {
  13. Scores []*Score
  14. }
  15. user := new(User)
  16. scores := g.Map{
  17. "Scores": g.Slice{
  18. g.Map{
  19. "Name": "john",
  20. "Result": 100,
  21. },
  22. g.Map{
  23. "Name": "smith",
  24. "Result": 60,
  25. },
  26. },
  27. }
  28. // 嵌套struct转换,属性为slice类型,数值为slice map类型
  29. if err := gconv.Struct(scores, user); err != nil {
  30. fmt.Println(err)
  31. } else {
  32. g.Dump(user)
  33. }
  34. }

执行后,输出结果为:

  1. {
  2. "Scores": [
  3. {
  4. "Name": "john",
  5. "Result": 100
  6. },
  7. {
  8. "Name": "smith",
  9. "Result": 60
  10. }
  11. ]
  12. }