【Go】利用 reflect 实现结构体设置默认值
写 API 时经常会需要结构体中某个参数拥有默认值。但如 Gin 只有 ShouldBindQuery
这种 form
类型支持设置默认值,常用的 ShouldBindJSON
这种 json
类型却不支持,很奇怪。
Gin 中 bind 结构体设置默认值
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()
}
请求会打印如下结果:
FormData: {Name:vksir Age:18}
JsonData:{Name: Age:0}
也就是说 JsonData
的默认值没有生效,如果看源码也可以发现 ShouldBindJSON
是没有设置默认值的动作的。
简单实现结构体设置默认值
编辑 structutil/struct.go
:
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
}
简单使用:
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)
}
{Name:vksir Age:18 Like:{Name:she Age:18}}
如期设置了默认值。
经常会发现 Go 利用 tag 完成了大量工作。
这点其实很奇怪,tag 本来就是类似注释一样的东西,但给了它太多意义。包括很多时候,注释也都利用起来了(像 embed
)。
个人感觉这是属于语言内容太少的原因,关键字少,内置函数少。
怪怪的。