0%

【Go】利用 reflect 实现结构体设置默认值

【Go】利用 reflect 实现结构体设置默认值

写 API 时经常会需要结构体中某个参数拥有默认值。但如 Gin 只有 ShouldBindQuery 这种 form 类型支持设置默认值,常用的 ShouldBindJSON 这种 json 类型却不支持,很奇怪。

Gin 中 bind 结构体设置默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
"fmt"
"github.com/gin-gonic/gin"
)

type FormData struct {
Name string `form:"name,default=vksir"`
Age int `form:"age,default=18"`
}

type JsonData struct {
Name string `json:"name,default=vksir"`
Age int `json:"age,default=18"`
}

func main() {
e := gin.Default()
e.GET("/", func(c *gin.Context) {
var fd FormData
c.ShouldBindQuery(&fd)
var jd JsonData
c.ShouldBindJSON(&jd)
fmt.Printf("FormData: %+v\n", fd)
fmt.Printf("JsonData:%+v\n", jd)
})
e.Run()
}

请求会打印如下结果:

1
2
FormData: {Name:vksir Age:18}
JsonData:{Name: Age:0}

也就是说 JsonData 的默认值没有生效,如果看源码也可以发现 ShouldBindJSON 是没有设置默认值的动作的。

简单实现结构体设置默认值

编辑 structutil/struct.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package structutil

import (
"reflect"
"strconv"
)

func SetDefault(v any) error {
typeOf := reflect.TypeOf(v).Elem()
valueOf := reflect.ValueOf(v).Elem()
return subSetDefault(typeOf, valueOf)
}

func subSetDefault(typeOf reflect.Type, valueOf reflect.Value) error {
for i := 0; i < typeOf.NumField(); i++ {
tField := typeOf.Field(i)
vField := valueOf.Field(i)
switch tField.Type.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
defaultVal, ok := tField.Tag.Lookup("default")
if !ok {
continue
}
defaultValInt, err := strconv.ParseInt(defaultVal, 10, 64)
if err != nil {
return err
}
vField.SetInt(defaultValInt)
case reflect.String:
defaultVal, ok := tField.Tag.Lookup("default")
if !ok {
continue
}
vField.SetString(defaultVal)
case reflect.Struct:
err := subSetDefault(tField.Type, vField)
if err != nil {
return err
}
}
}
return nil
}

简单使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"GoCode/structutil"
"encoding/json"
"fmt"
)

type Person struct {
Name string `json:"name" default:"vksir"`
Age int `json:"age" default:"18"`
Like struct {
Name string `json:"name" default:"she"`
Age int `json:"age" default:"18"`
} `json:"like"`
}

func main() {
var p Person
rawData := `{"like": {}}`
structutil.SetDefault(&p)
json.Unmarshal([]byte(rawData), &p)
fmt.Printf("%+v\n", p)
}
1
{Name:vksir Age:18 Like:{Name:she Age:18}}

如期设置了默认值。


经常会发现 Go 利用 tag 完成了大量工作。

这点其实很奇怪,tag 本来就是类似注释一样的东西,但给了它太多意义。包括很多时候,注释也都利用起来了(像 embed)。

个人感觉这是属于语言内容太少的原因,关键字少,内置函数少。

怪怪的。