123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- package reflect
- import (
- "fmt"
- "reflect"
- "strconv"
- "strings"
- )
- var (
- allowTags = []string{"json", "yaml", "xml", "name"}
- )
- func findField(v reflect.Value, field string) reflect.Value {
- var (
- pos int
- tagValue string
- refType reflect.Type
- fieldType reflect.StructField
- )
- refType = v.Type()
- for i := 0; i < refType.NumField(); i++ {
- fieldType = refType.Field(i)
- for _, tagName := range allowTags {
- tagValue = fieldType.Tag.Get(tagName)
- if tagValue == "" {
- continue
- }
- if pos = strings.IndexByte(tagValue, ','); pos != -1 {
- tagValue = tagValue[:pos]
- }
- if tagValue == field {
- return v.Field(i)
- }
- }
- }
- return v.FieldByName(field)
- }
- func safeAssignment(variable reflect.Value, value interface{}) (err error) {
- var (
- n int64
- un uint64
- fn float64
- kind reflect.Kind
- )
- rv := reflect.ValueOf(value)
- kind = variable.Kind()
- if kind != reflect.Slice && kind != reflect.Array && kind != reflect.Map && kind == rv.Kind() {
- variable.Set(rv)
- return
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- switch rv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- variable.SetInt(rv.Int())
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- variable.SetInt(int64(rv.Uint()))
- case reflect.Float32, reflect.Float64:
- variable.SetInt(int64(rv.Float()))
- case reflect.String:
- if n, err = strconv.ParseInt(rv.String(), 10, 64); err == nil {
- variable.SetInt(n)
- }
- default:
- err = fmt.Errorf("unsupported kind %s", rv.Kind())
- }
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- switch rv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- variable.SetUint(uint64(rv.Int()))
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- variable.SetUint(rv.Uint())
- case reflect.Float32, reflect.Float64:
- variable.SetUint(uint64(rv.Float()))
- case reflect.String:
- if un, err = strconv.ParseUint(rv.String(), 10, 64); err == nil {
- variable.SetUint(un)
- }
- default:
- err = fmt.Errorf("unsupported kind %s", rv.Kind())
- }
- case reflect.Float32, reflect.Float64:
- switch rv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- variable.SetFloat(float64(rv.Int()))
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- variable.SetFloat(float64(rv.Uint()))
- case reflect.Float32, reflect.Float64:
- variable.SetFloat(rv.Float())
- case reflect.String:
- if fn, err = strconv.ParseFloat(rv.String(), 64); err == nil {
- variable.SetFloat(fn)
- }
- default:
- err = fmt.Errorf("unsupported kind %s", rv.Kind())
- }
- case reflect.String:
- switch rv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- variable.SetString(strconv.FormatInt(rv.Int(), 10))
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- variable.SetString(strconv.FormatUint(rv.Uint(), 10))
- case reflect.Float32, reflect.Float64:
- variable.SetString(strconv.FormatFloat(rv.Float(), 'f', -1, 64))
- case reflect.String:
- variable.SetString(rv.String())
- default:
- variable.SetString(fmt.Sprint(value))
- }
- default:
- err = fmt.Errorf("unsupported kind %s", kind)
- }
- return
- }
- func Set(hacky interface{}, field string, value interface{}) (err error) {
- var (
- n int
- refField reflect.Value
- )
- refVal := reflect.ValueOf(hacky)
- if refVal.Kind() == reflect.Ptr {
- refVal = reflect.Indirect(refVal)
- }
- if refVal.Kind() != reflect.Struct {
- return fmt.Errorf("%s kind is %v", refVal.Type().String(), refField.Kind())
- }
- refField = findField(refVal, field)
- if !refField.IsValid() {
- return fmt.Errorf("%s field `%s` not found", refVal.Type(), field)
- }
- rv := reflect.ValueOf(value)
- fieldKind := refField.Kind()
- if fieldKind != reflect.Slice && fieldKind != reflect.Array && fieldKind != reflect.Map && fieldKind == rv.Kind() {
- refField.Set(rv)
- return
- }
- switch fieldKind {
- case reflect.Struct:
- switch rv.Kind() {
- case reflect.Map:
- keys := rv.MapKeys()
- subVal := reflect.New(refField.Type())
- for _, key := range keys {
- pv := rv.MapIndex(key)
- if key.Kind() == reflect.String {
- if err = Set(subVal.Interface(), key.String(), pv.Interface()); err != nil {
- return err
- }
- }
- }
- refField.Set(subVal.Elem())
- default:
- err = fmt.Errorf("struct unsupported assign kind %s", rv.Kind())
- }
- case reflect.Ptr:
- elemType := refField.Type()
- if elemType.Elem().Kind() == reflect.Struct {
- switch rv.Kind() {
- case reflect.Map:
- keys := rv.MapKeys()
- subVal := reflect.New(elemType.Elem())
- for _, key := range keys {
- pv := rv.MapIndex(key)
- if key.Kind() == reflect.String {
- if err = Set(subVal.Interface(), key.String(), pv.Interface()); err != nil {
- return err
- }
- }
- }
- refField.Set(subVal)
- default:
- err = fmt.Errorf("struct unsupported assign kind %s", rv.Kind())
- }
- } else {
- err = fmt.Errorf("ptr can't set kind %s", elemType.Elem().Kind())
- }
- case reflect.Array, reflect.Slice:
- innerType := refField.Type().Elem()
- if rv.Kind() == reflect.Array || rv.Kind() == reflect.Slice {
- sliceVar := reflect.MakeSlice(refField.Type(), rv.Len(), rv.Len())
- n = 0
- for i := 0; i < rv.Len(); i++ {
- srcVal := rv.Index(i)
- dstVal := reflect.New(innerType).Elem()
- if err = safeAssignment(dstVal, srcVal); err == nil {
- sliceVar.Index(n).Set(dstVal)
- n++
- }
- }
- refField.Set(sliceVar.Slice(0, n))
- }
- default:
- err = safeAssignment(refField, value)
- }
- return
- }
|