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:
params
is the variable parameter to be converted to astruct
. It can be of any data type, commonly amap
.pointer
is the targetstruct
object for conversion. This parameter must be a pointer to thestruct
object; the object's attributes will be updated upon successful conversion.mapping
is a custom mapping betweenmap key name
andstruct attribute
. In this case, theparams
parameter must be ofmap
type, 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
struct
that need to be matched must be public attributes (capitalized first letter). - Depending on the type of
params
, the logic varies:- If
params
is of typemap
: 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 thestruct
. - Additionally, if the attribute of the
struct
is 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
mapping
parameter is not empty, it maps according to thekey
tostruct field name
relationship. - If a field
tag
is set, it will use thetag
to match thekey
of theparams
parameter. If notag
is set, gconv will look for the fieldtag
in the order ofgconv, param, c, p, json
. - Match according to
field name
. - If none of the above matches, gconv will iterate through all
key
s 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,
},
}