• Struct转换
    • 转换规则
    • 自动初始化
    • Struct递归转换
    • 示例1,基本使用
    • 示例2,复杂类型
      • 1. slice类型属性
      • 2. struct属性为struct/*struct
      • 3. struct属性为slice,数值为slice
      • 4. struct属性为slice,数值为非slice
      • 5. struct属性为[]*struct

    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方法内部将会自动进行初始化创建对象,并修改传递变量指向的指针地址。

    1. package main
    2. import (
    3. "github.com/gogf/gf/g"
    4. "github.com/gogf/gf/g/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/g"
    4. "github.com/gogf/gf/g/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,基本使用

    package main
    
    import (
        "github.com/gogf/gf/g"
        "github.com/gogf/gf/g/util/gconv"
    )
    
    type User struct {
        Uid      int
        Name     string
        Site_Url string
        NickName string
        Pass1    string `gconv:"password1"`
        Pass2    string `gconv:"password2"`
    }
    
    func main() {
        user    := (*User)(nil)
    
        // 使用默认映射规则绑定属性值到对象
        user     = new(User)
        params1 := g.Map{
            "uid"       : 1,
            "Name"      : "john",
            "siteurl"   : "https://goframe.org",
            "nick_name" : "johng",
            "PASS1"     : "123",
            "PASS2"     : "456",
        }
        if err := gconv.Struct(params1, user); err == nil {
            g.Dump(user)
        }
    
        // 使用struct tag映射绑定属性值到对象
        user     = new(User)
        params2 := g.Map {
            "uid"       : 2,
            "name"      : "smith",
            "site-url"  : "https://goframe.org",
            "nick name" : "johng",
            "password1" : "111",
            "password2" : "222",
        }
        if err := gconv.Struct(params2, user); err == nil {
            g.Dump(user)
        }
    }
    

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

    执行后,输出结果为:

    {
        "Uid": 1,
        "Name": "john",
        "Site_Url": "https://goframe.org",
        "NickName": "johng",
        "Pass1": "123",
        "Pass2": "456"
    }
    {
        "Uid": 2,
        "Name": "smith",
        "Site_Url": "https://goframe.org",
        "NickName": "johng",
        "Pass1": "111",
        "Pass2": "222"
    }
    

    示例2,复杂类型

    1. slice类型属性

    package main
    
    import (
        "github.com/gogf/gf/g/util/gconv"
        "github.com/gogf/gf/g"
        "fmt"
    )
    
    // 演示slice类型属性的赋值
    func main() {
        type User struct {
            Scores []int
        }
    
        user   := new(User)
        scores := []interface{}{99, 100, 60, 140}
    
        // 通过map映射转换
        if err := gconv.Struct(g.Map{"Scores" : scores}, user); err != nil {
            fmt.Println(err)
        } else {
            g.Dump(user)
        }
    
        // 通过变量映射转换,直接slice赋值
        if err := gconv.Struct(scores, user); err != nil {
            fmt.Println(err)
        } else {
            g.Dump(user)
        }
    }
    

    执行后,输出结果为:

    {
        "Scores": [
            99,
            100,
            60,
            140
        ]
    }
    {
        "Scores": [
            99,
            100,
            60,
            140
        ]
    }
    

    2. struct属性为struct/*struct

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

    package main
    
    import (
        "github.com/gogf/gf/g/util/gconv"
        "github.com/gogf/gf/g"
        "fmt"
    )
    
    func main() {
        type Score struct {
            Name   string
            Result int
        }
        type User1 struct {
            Scores Score
        }
        type User2 struct {
            Scores *Score
        }
    
        user1  := new(User1)
        user2  := new(User2)
        scores := map[string]interface{}{
            "Scores" : map[string]interface{}{
                "Name"   : "john",
                "Result" : 100,
            },
        }
    
        if err := gconv.Struct(scores, user1); err != nil {
            fmt.Println(err)
        } else {
            g.Dump(user1)
        }
        if err := gconv.Struct(scores, user2); err != nil {
            fmt.Println(err)
        } else {
            g.Dump(user2)
        }
    }
    

    执行后,输出结果为:

    {
        "Scores": {
            "Name": "john",
            "Result": 100
        }
    }
    {
        "Scores": {
            "Name": "john",
            "Result": 100
        }
    }
    

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

    package main
    
    import (
        "github.com/gogf/gf/g/util/gconv"
        "github.com/gogf/gf/g"
        "fmt"
    )
    
    func main() {
        type Score struct {
            Name   string
            Result int
        }
        type User struct {
            Scores []Score
        }
    
        user   := new(User)
        scores := map[string]interface{}{
            "Scores" : []interface{}{
                map[string]interface{}{
                    "Name"   : "john",
                    "Result" : 100,
                },
                map[string]interface{}{
                    "Name"   : "smith",
                    "Result" : 60,
                },
            },
        }
    
        // 嵌套struct转换,属性为slice类型,数值为slice map类型
        if err := gconv.Struct(scores, user); err != nil {
            fmt.Println(err)
        } else {
            g.Dump(user)
        }
    }
    

    执行后,输出结果为:

    {
        "Scores": [
            {
                "Name": "john",
                "Result": 100
            },
            {
                "Name": "smith",
                "Result": 60
            }
        ]
    }
    

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

    package main
    
    import (
        "github.com/gogf/gf/g/util/gconv"
        "github.com/gogf/gf/g"
        "fmt"
    )
    
    func main() {
        type Score struct {
            Name   string
            Result int
        }
        type User struct {
            Scores []Score
        }
    
        user   := new(User)
        scores := map[string]interface{}{
            "Scores" : map[string]interface{}{
                "Name"   : "john",
                "Result" : 100,
            },
        }
    
        // 嵌套struct转换,属性为slice类型,数值为map类型
        if err := gconv.Struct(scores, user); err != nil {
            fmt.Println(err)
        } else {
            g.Dump(user)
        }
    }
    

    执行后,输出结果为:

    {
        "Scores": [
            {
                "Name": "john",
                "Result": 100
            }
        ]
    }
    

    5. struct属性为[]*struct

    package main
    
    import (
        "github.com/gogf/gf/g/util/gconv"
        "github.com/gogf/gf/g"
        "fmt"
    )
    
    func main() {
        type Score struct {
            Name   string
            Result int
        }
        type User struct {
            Scores []*Score
        }
    
        user   := new(User)
        scores := map[string]interface{}{
            "Scores" : []interface{}{
                map[string]interface{}{
                    "Name"   : "john",
                    "Result" : 100,
                },
                map[string]interface{}{
                    "Name"   : "smith",
                    "Result" : 60,
                },
            },
        }
    
        // 嵌套struct转换,属性为slice类型,数值为slice map类型
        if err := gconv.Struct(scores, user); err != nil {
            fmt.Println(err)
        } else {
            g.Dump(user)
        }
    }
    

    执行后,输出结果为:

    {
        "Scores": [
            {
                "Name": "john",
                "Result": 100
            },
            {
                "Name": "smith",
                "Result": 60
            }
        ]
    }