如何使用反射,给结构体赋值

1,743 阅读1分钟

需求示例代码

type Student struct {
	Name *string `db:"name"`
	Age  int64   `db:"age"`
}
value := map[string]interface{} {"name": "zhangsan", "age": 10}

需要通过上述的value来获得一个对应的Student对象。

func Copy(data map[string]interface{}, target interface{}) {
	_value := reflect.ValueOf(target).Elem()
	_type := reflect.TypeOf(target).Elem()
	// 完成tag到域索引的映射
	nameMap := make(map[string]int)
	for i := 0; i < _value.NumField(); i++ {
		nameMap[_type.Field(i).Tag.Get("db")] = i
	}
	for key, value := range data {
		if idx, ok := nameMap[key]; ok {
			// 检测变量类型,要判断struct属性的类型,是否是指针
			if _value.Field(idx).Kind() == reflect.Ptr &&
				_type.Field(idx).Type.String() == "*"+reflect.TypeOf(value).String() {
				switch value.(type) {
				case int:
					tmpVal := value.(int)
					_value.Field(idx).Set(reflect.ValueOf(&tmpVal))
				case int32:
					tmpVal := value.(int32)
					_value.Field(idx).Set(reflect.ValueOf(&tmpVal))
				case int64:
					tmpVal := value.(int64)
					_value.Field(idx).Set(reflect.ValueOf(&tmpVal))
				case string:
					tmpVal := value.(string)
					_value.Field(idx).Set(reflect.ValueOf(&tmpVal))
				case bool:
					tmpVal := value.(bool)
					_value.Field(idx).Set(reflect.ValueOf(&tmpVal))
				case float64:
					tmpVal := value.(float64)
					_value.Field(idx).Set(reflect.ValueOf(&tmpVal))
				default:
          // 可用于确定未添加的参数,也可以是一个log,后面可以再通过log补上基本类型
					panic(errors.New(fmt.Sprintf("未知基本类型%T\n", value)))
				}
			} else if _type.Field(idx).Type.String() == reflect.TypeOf(value).String() {
				_value.Field(idx).Set(reflect.ValueOf(value))
			} else {
        // 可用于确定未添加的参数,也可以是一个log,然后最后再添加上
				panic(errors.New(fmt.Sprintf("key「%v」value %v」; type 「%v」 => 「%v」\n",
					key,
					value,
					reflect.TypeOf(value).String(),
					_type.Field(idx).Type.String())))
			}
		}
	}
}

正常赋值的操作

b := map[string]interface{}{"name": "zhangsan", "age": int64(10)}
student := new(Student)
Copy(b, student)