[英]Get pointers to all fields of a struct dynamically using reflection
我正在尝试为 golang 构建一个简单的 orm 层。 这将采用一个结构并生成cols []
然后可以将其传递给 sql function rows.Scan(cols...)
它采用结构中与它在结果集中找到的每一列相对应的字段指针
这是我的示例结构
type ExampleStruct struct {
ID int64 `sql:"id"`
aID string `sql:"a_id"`
UserID int64 `sql:"user_id"`
这是我的通用 ORM function
func GetSqlColumnToFieldMap(model *ExampleStruct) map[string]interface{} {
typeOfModel := reflect.TypeOf(*model)
ValueOfModel := reflect.ValueOf(*model)
columnToDataPointerMap := make(map[string]interface{})
for i := 0; i < ValueOfModel.NumField(); i++ {
sql_column := typeOfModel.Field(i).Tag.Get("sql")
structValue := ValueOfModel.Field(i)
columnToDataPointerMap[sql_column] = structValue.Addr()
}
return columnToDataPointerMap
}
一旦此方法正常工作,我可以使用它生成的 map 根据我在 rows() 中获得的 column_names 创建一个有序的 sql 指针列表 object 但是我在.Addr()
方法调用中遇到以下错误
panic: reflect.Value.Addr of unaddressable value [recovered]
panic: reflect.Value.Addr of unaddressable value
不可能这样做吗? 同样在理想情况下,我希望该方法采用接口而不是 *ExampleStruct,以便它可以在不同的数据库模型中重用。
该错误表明您想要获取其地址的值是不可寻址的。 这是因为即使您将指针传递给GetSqlColumnToFieldMap()
,您也会立即取消引用它并稍后使用非指针值。
当传递给reflect.ValueOf()
时,此值被包装在interface{}
中,并且包装在接口中的值不可寻址。
您不能取消引用指针,而是使用Type.Elem()
和Value.Elem()
来获取元素类型和指向的值。
是这样的:
func GetSqlColumnToFieldMap(model *ExampleStruct) map[string]interface{} {
t := reflect.TypeOf(model).Elem()
v := reflect.ValueOf(model).Elem()
columnToDataPointerMap := make(map[string]interface{})
for i := 0; i < v.NumField(); i++ {
sql_column := t.Field(i).Tag.Get("sql")
structValue := v.Field(i)
columnToDataPointerMap[sql_column] = structValue.Addr()
}
return columnToDataPointerMap
}
通过这个简单的更改它就可以工作,并且它不依赖于参数类型,您可以将其更改为interface{}
并传递任何结构指针。
func GetSqlColumnToFieldMap(model interface{}) map[string]interface{} {
// ...
}
测试它:
type ExampleStruct struct {
ID int64 `sql:"id"`
AID string `sql:"a_id"`
UserID int64 `sql:"user_id"`
}
type Point struct {
X int `sql:"x"`
Y int `sql:"y"`
}
func main() {
fmt.Println(GetSqlColumnToFieldMap(&ExampleStruct{}))
fmt.Println(GetSqlColumnToFieldMap(&Point{}))
}
Output(在Go 游乐场试试):
map[a_id:<*string Value> id:<*int64 Value> user_id:<*int64 Value>]
map[x:<*int Value> y:<*int Value>]
请注意, Value.Addr()
返回包裹在reflect.Value
中的地址。 要“展开”指针,请使用Value.Interface()
:
func GetSqlColumnToFieldMap(model interface{}) map[string]interface{} {
t := reflect.TypeOf(model).Elem()
v := reflect.ValueOf(model).Elem()
m := make(map[string]interface{})
for i := 0; i < v.NumField(); i++ {
colName := t.Field(i).Tag.Get("sql")
field := v.Field(i)
m[colName] = field.Addr().Interface()
}
return m
}
这将是 output(在Go 游乐场上试试):
map[a_id:0xc00007e008 id:0xc00007e000 user_id:0xc00007e018]
map[x:0xc000018060 y:0xc000018068]
关于反射的深入介绍,请阅读博文: The Laws of Reflection
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.