In projects, we frequently encounter the use of a large number of struct, and conversions/assignments from various data types to struct (especially json/xml/various protocol encoding conversions). To improve coding and project maintenance efficiency, the gconv module provides developers with substantial benefits by offering greater flexibility in data parsing.

The gconv module performs struct type conversion through the Struct method, defined as follows:

  1. // Struct maps the params key-value pairs to the corresponding struct object's attributes.
  2. // The third parameter `mapping` is unnecessary, indicating the mapping rules between the
  3. // custom key name and the attribute name(case sensitive).
  4. //
  5. // Note:
  6. // 1. The `params` can be any type of map/struct, usually a map.
  7. // 2. The `pointer` should be type of *struct/**struct, which is a pointer to struct object
  8. // or struct pointer.
  9. // 3. Only the public attributes of struct object can be mapped.
  10. // 4. If `params` is a map, the key of the map `params` can be lowercase.
  11. // It will automatically convert the first letter of the key to uppercase
  12. // in mapping procedure to do the matching.
  13. // It ignores the map key, if it does not match.
  14. func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error)

Where:

  1. params is the variable parameter to be converted to a struct. It can be of any data type, commonly a map.
  2. pointer is the target struct object for conversion. This parameter must be a pointer to the struct object; the object’s attributes will be updated upon successful conversion.
  3. mapping is a custom mapping between map key name and struct attribute. In this case, the params parameter must be of map type, otherwise the parameter is meaningless. In most cases, this parameter can be omitted, using the default conversion rules instead.

Type Conversion - Struct - 图1tip

For more struct related conversion methods, please refer to the interface documentation: https://pkg.go.dev/github.com/gogf/gf/v2/util/gconv

Conversion Rules

The gconv module’s struct conversion feature is powerful, supporting mapping conversion from any data type to struct attributes. Without custom mapping conversion rules, the default conversion rules are as follows:

  1. Attributes in the struct that need to be matched must be public attributes (capitalized first letter).
  2. Depending on the type of params, the logic varies:
    • If params is of type map: The key name will be automatically matched to the struct attribute in a case-insensitive and special character ignored manner.
    • If params is of another type: The value will be matched against the first attribute of the struct.
    • Additionally, if the attribute of the struct is a complex data type such as slice, map, struct, recursive matching and assignment will be performed.
  3. If matching is successful, the key value is assigned to the attribute, otherwise the key value is ignored.

Matching Rules Priority (only for map to struct conversion)

  1. If the mapping parameter is not empty, it maps according to the key to struct field name relationship.
  2. If a field tag is set, it will use the tag to match the key of the params parameter. If no tag is set, gconv will look for the field tag in the order of gconv, param, c, p, json.
  3. Match according to field name.
  4. If none of the above matches, gconv will iterate through all keys in params, matching according to the following rules:
    • Field name: ignore case and underscores
    • Key: ignore case, underscores, and special characters

Tip

Type Conversion - Struct - 图2warning

Unless there are special circumstances, please try to satisfy the first three rules as the fourth rule is less performant.

Here are some examples of map key names and struct attribute names:

  1. map key struct attribute match?
  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

Automatic Object Creation

When the given pointer parameter type is **struct, the Struct method will automatically create the struct object and modify the pointer address to which the variable points.

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

After execution, the output is:

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

Struct Recursive Conversion

Recursive conversion refers to the capability to map params parameter data (the first parameter) recursively onto sub-objects when a struct object contains sub-objects that are defined in an embedded manner. It is often used in struct objects with inherited objects.

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/frame/g"
  4. "github.com/gogf/gf/v2/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.Struct(data, user)
  31. g.Dump(user)
  32. }

After execution, the output in the terminal is:

  1. {
  2. Id: 1,
  3. Uid: 100,
  4. CreateTime: "2019",
  5. Passport: "john",
  6. Password: "123456",
  7. Nickname: "John",
  8. }

Example 1: Basic Usage

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/frame/g"
  4. "github.com/gogf/gf/v2/util/gconv"
  5. )
  6. type User struct {
  7. Uid int
  8. Name string
  9. SiteUrl string
  10. NickName string
  11. Pass1 string `c:"password1"`
  12. Pass2 string `c:"password2"`
  13. }
  14. func main() {
  15. var user *User
  16. // Use default mapping rules to bind attribute values to objects
  17. user = new(User)
  18. params1 := g.Map{
  19. "uid": 1,
  20. "Name": "john",
  21. "site_url": "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. // Use struct tag mapping to bind attribute values to objects
  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. }

As seen, you can directly bind a map to a struct using the Struct method with default rules or flexibly configure using the struct tag. Additionally, the Struct method has the third map parameter to specify custom parameter name to attribute name mappings.

After execution, the output is:

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

Example 2: Complex Attribute Types

Attributes support conversion of struct objects or struct object pointers (if the target is a pointer and nil, it will be initialized during conversion).

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/util/gconv"
  4. "github.com/gogf/gf/v2/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. }

After execution, the output is:

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