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.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 }