package reflect import ( "errors" "fmt" "reflect" "strconv" "strings" ) var ( allowTags = []string{"json", "yaml", "xml", "name"} ) var ( ErrValueAssociated = errors.New("value cannot be associated") ) 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 Assign(variable reflect.Value, value interface{}) (err error) { return safeAssignment(variable, value) } 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.Bool: switch rv.Kind() { case reflect.Bool: variable.SetBool(rv.Bool()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if rv.Int() != 0 { variable.SetBool(true) } else { variable.SetBool(false) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if rv.Uint() != 0 { variable.SetBool(true) } else { variable.SetBool(false) } case reflect.Float32, reflect.Float64: if rv.Float() != 0 { variable.SetBool(true) } else { variable.SetBool(false) } case reflect.String: var tv bool tv, err = strconv.ParseBool(rv.String()) if err == nil { variable.SetBool(tv) } default: err = fmt.Errorf("boolean value can not assign %s", rv.Kind()) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: switch rv.Kind() { case reflect.Bool: if rv.Bool() { variable.SetInt(1) } else { variable.SetInt(0) } 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("integer value can not assign %s", rv.Kind()) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: switch rv.Kind() { case reflect.Bool: if rv.Bool() { variable.SetUint(1) } else { variable.SetUint(0) } 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("unsigned integer value can not assign %s", rv.Kind()) } case reflect.Float32, reflect.Float64: switch rv.Kind() { case reflect.Bool: if rv.Bool() { variable.SetFloat(1) } else { variable.SetFloat(0) } 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("decimal value can not assign %s", rv.Kind()) } case reflect.String: switch rv.Kind() { case reflect.Bool: if rv.Bool() { variable.SetString("true") } else { variable.SetString("false") } 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)) } case reflect.Interface: variable.Set(rv) 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: if rv.Kind() != reflect.Map { return ErrValueAssociated } 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()) case reflect.Ptr: elemType := refField.Type() if elemType.Elem().Kind() != reflect.Struct { return ErrValueAssociated } else { if rv.Kind() != reflect.Map { return ErrValueAssociated } 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) } case reflect.Map: if rv.Kind() != reflect.Map { return ErrValueAssociated } targetValue := reflect.MakeMap(refField.Type()) keys := rv.MapKeys() for _, key := range keys { pv := rv.MapIndex(key) kVal := reflect.New(refField.Type().Key()) eVal := reflect.New(refField.Type().Elem()) if err = safeAssignment(kVal.Elem(), key.Interface()); err != nil { return ErrValueAssociated } if refField.Type().Elem().Kind() == reflect.Struct { if pv.Elem().Kind() != reflect.Map { return ErrValueAssociated } subKeys := pv.Elem().MapKeys() for _, subKey := range subKeys { subVal := pv.Elem().MapIndex(subKey) if subKey.Kind() == reflect.String { if err = Set(eVal.Interface(), subKey.String(), subVal.Interface()); err != nil { return err } } } targetValue.SetMapIndex(kVal.Elem(), eVal.Elem()) } else { if err = safeAssignment(eVal.Elem(), pv.Interface()); err != nil { return ErrValueAssociated } targetValue.SetMapIndex(kVal.Elem(), eVal.Elem()) } } refField.Set(targetValue) case reflect.Array, reflect.Slice: n = 0 innerType := refField.Type().Elem() if rv.Kind() == reflect.Array || rv.Kind() == reflect.Slice { if innerType.Kind() == reflect.Struct { sliceVar := reflect.MakeSlice(refField.Type(), rv.Len(), rv.Len()) for i := 0; i < rv.Len(); i++ { srcVal := rv.Index(i) if srcVal.Kind() != reflect.Map { return ErrValueAssociated } dstVal := reflect.New(innerType) keys := srcVal.MapKeys() for _, key := range keys { kv := srcVal.MapIndex(key) if key.Kind() == reflect.String { if err = Set(dstVal.Interface(), key.String(), kv.Interface()); err != nil { return } } } sliceVar.Index(n).Set(dstVal.Elem()) n++ } refField.Set(sliceVar.Slice(0, n)) } else if innerType.Kind() == reflect.Ptr { sliceVar := reflect.MakeSlice(refField.Type(), rv.Len(), rv.Len()) for i := 0; i < rv.Len(); i++ { srcVal := rv.Index(i) if srcVal.Kind() != reflect.Map { return ErrValueAssociated } dstVal := reflect.New(innerType.Elem()) keys := srcVal.MapKeys() for _, key := range keys { kv := srcVal.MapIndex(key) if key.Kind() == reflect.String { if err = Set(dstVal.Interface(), key.String(), kv.Interface()); err != nil { return } } } sliceVar.Index(n).Set(dstVal) n++ } refField.Set(sliceVar.Slice(0, n)) } else { sliceVar := reflect.MakeSlice(refField.Type(), rv.Len(), rv.Len()) for i := 0; i < rv.Len(); i++ { srcVal := rv.Index(i) dstVal := reflect.New(innerType).Elem() if err = safeAssignment(dstVal, srcVal.Interface()); err != nil { return } sliceVar.Index(n).Set(dstVal) n++ } refField.Set(sliceVar.Slice(0, n)) } } default: err = safeAssignment(refField, value) } return }