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:
// Struct maps the params key-value pairs to the corresponding struct object's attributes.
// The third parameter `mapping` is unnecessary, indicating the mapping rules between the
// custom key name and the attribute name(case sensitive).
//
// Note:
// 1. The `params` can be any type of map/struct, usually a map.
// 2. The `pointer` should be type of *struct/**struct, which is a pointer to struct object
// or struct pointer.
// 3. Only the public attributes of struct object can be mapped.
// 4. If `params` is a map, the key of the map `params` can be lowercase.
// It will automatically convert the first letter of the key to uppercase
// in mapping procedure to do the matching.
// It ignores the map key, if it does not match.
func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error)
Where:
paramsis the variable parameter to be converted to astruct. It can be of any data type, commonly amap.pointeris the targetstructobject for conversion. This parameter must be a pointer to thestructobject; the object's attributes will be updated upon successful conversion.mappingis a custom mapping betweenmap key nameandstruct attribute. In this case, theparamsparameter must be ofmaptype, otherwise the parameter is meaningless. In most cases, this parameter can be omitted, using the default conversion rules instead.
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:
- Attributes in the
structthat need to be matched must be public attributes (capitalized first letter). - Depending on the type of
params, the logic varies:- If
paramsis of typemap: The key name will be automatically matched to the struct attribute in a case-insensitive and special character ignored manner. - If
paramsis of another type: The value will be matched against the first attribute of thestruct. - Additionally, if the attribute of the
structis a complex data type such asslice,map,struct, recursive matching and assignment will be performed.
- If
- 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)
- If the
mappingparameter is not empty, it maps according to thekeytostruct field namerelationship. - If a field
tagis set, it will use thetagto match thekeyof theparamsparameter. If notagis set, gconv will look for the fieldtagin the order ofgconv, param, c, p, json. - Match according to
field name. - If none of the above matches, gconv will iterate through all
keys inparams, matching according to the following rules:Field name: ignore case and underscoresKey: ignore case, underscores, and special characters
Tip
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:
map key struct attribute match?
name Name match
Email Email match
nickname NickName match
NICKNAME NickName match
Nick-Name NickName match
nick_name NickName match
nick name NickName match
NickName Nick_Name match
Nick-name Nick_Name match
nick_name Nick_Name match
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.
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
func main() {
type User struct {
Uid int
Name string
}
params := g.Map{
"uid": 1,
"name": "john",
}
var user *User
if err := gconv.Struct(params, &user); err != nil {
panic(err)
}
g.Dump(user)
}
After execution, the output is:
{
Uid: 1,
Name: "john",
}
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.
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
func main() {
type Ids struct {
Id int `json:"id"`
Uid int `json:"uid"`
}
type Base struct {
Ids
CreateTime string `json:"create_time"`
}
type User struct {
Base
Passport string `json:"passport"`
Password string `json:"password"`
Nickname string `json:"nickname"`
}
data := g.Map{
"id" : 1,
"uid" : 100,
"passport" : "john",
"password" : "123456",
"nickname" : "John",
"create_time" : "2019",
}
user := new(User)
gconv.Struct(data, user)
g.Dump(user)
}
After execution, the output in the terminal is:
{
Id: 1,
Uid: 100,
CreateTime: "2019",
Passport: "john",
Password: "123456",
Nickname: "John",
}
Example 1: Basic Usage
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
type User struct {
Uid int
Name string
SiteUrl string
NickName string
Pass1 string `c:"password1"`
Pass2 string `c:"password2"`
}
func main() {
var user *User
// Use default mapping rules to bind attribute values to objects
user = new(User)
params1 := g.Map{
"uid": 1,
"Name": "john",
"site_url": "https://goframe.org",
"nick_name": "johng",
"PASS1": "123",
"PASS2": "456",
}
if err := gconv.Struct(params1, user); err == nil {
g.Dump(user)
}
// Use struct tag mapping to bind attribute values to objects
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)
}
}
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:
{
Uid: 1,
Name: "john",
SiteUrl: "https://goframe.org",
NickName: "johng",
Pass1: "123",
Pass2: "456",
}
{
Uid: 2,
Name: "smith",
SiteUrl: "https://goframe.org",
NickName: "johng",
Pass1: "111",
Pass2: "222",
}
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).
package main
import (
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/frame/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 := g.Map{
"Scores": g.Map{
"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)
}
}
After execution, the output is:
{
Scores: {
Name: "john",
Result: 100,
},
}
{
Scores: {
Name: "john",
Result: 100,
},
}