代码片段: RequestToStruct
工作中写到的一段通用性较高的代码,根据 结构体 A 中的 tag 匹配数据到 结构体B
package controller
import (
"bibibi/logger"
"errors"
"reflect"
"strconv"
"github.com/shopspring/decimal"
)
type Request struct {
}
// ToStruct 将请求结构体 `req` 中包含 `to` 标签的字段赋值给 `target` 结构体
func ToStruct(req interface{}, target interface{}) error {
targetType := reflect.TypeOf(target)
if targetType.Kind() != reflect.Ptr {
return errors.New("target must be reflect.Ptr")
}
reqType := reflect.TypeOf(req)
reqValue := reflect.ValueOf(req)
targetValue := reflect.ValueOf(target)
targetElem := targetValue.Elem()
for i := 0; i < reqType.NumField(); i++ {
reqField := reqType.Field(i) //
toName := reqField.Tag.Get("to")
if toName == "" || toName == "-" {
continue
}
reqVal := reqValue.FieldByName(reqField.Name)
//fmt.Println("请求结构体---> i:", i, ",toName :", toName, ",reqVal:", reqVal)
targetField := targetElem.FieldByName(toName)
if !targetField.CanSet() {
logger.L.Error("targetField.CanSet() is false!")
continue
}
switch targetField.Kind() { // 按类型给目标变量设置值
case reflect.String:
if reqVal.Kind() == reflect.String {
targetField.SetString(reqVal.String())
} else if reqVal.Kind() == reflect.Int {
targetField.SetString(strconv.Itoa(int(reqVal.Int())))
}
case reflect.Int:
if reqVal.Kind() == reflect.String {
num, e := strconv.Atoi(reqVal.String())
if e != nil {
logger.L.Error("RequestTo(): strconv.Atoi() fail:", e)
continue
}
targetField.SetInt(int64(num))
} else if reqVal.Kind() == reflect.Int {
targetField.SetInt(reqVal.Int())
}
case reflect.Struct:
if targetField.Type().String() != "decimal.Decimal" {
logger.L.Error("RequestTo() `to`对应目标字段是 `decimal.Decimal` 以外的结构体:", targetField.Type().String())
continue
}
if reqVal.Kind() == reflect.String {
_v, e := decimal.NewFromString(reqVal.String())
if e != nil {
logger.L.Error("RequestTo(): decimal.NewFromString() fail:", e)
continue
}
targetField.Set(reflect.ValueOf(_v))
}
default:
logger.L.Error("RequestTo(): targetField.Kind()-->", targetField.Kind())
}
}
return nil
}