【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 mainimport ( "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 structutilimport ( "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 mainimport ( "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
)。
个人感觉这是属于语言内容太少的原因,关键字少,内置函数少。
怪怪的。