反射以获取字段标记
Posted
技术标签:
【中文标题】反射以获取字段标记【英文标题】:Reflection to get field tag 【发布时间】:2021-11-01 22:22:42 【问题描述】:在 Go 中是否有一种使用反射来获取字段标记的好方法,只需将字段包装在反射库中的函数中?
我基本上是在尝试创建一个精简的数据访问对象,它允许在数据库中获取列名,而无需在整个地方进行硬编码。
以下是以 db 列名作为标签的结构。
// Table Structures
type CusipTableRow struct
Id int64 `db:"id"`
Cusip string `db:"cusip"`
Symbol string `db:"symbol"`
Active int8 `db:"active"`
Added_Time int32 `db:"added_timestamp"`
Description string `db:"description"`
Exchange string `db:"exchange"`
AssetType string `db:"asset_type"`
我正在寻求一个建议,而不是下载另一个库,以了解如何使用反射来本质上进行这样的调用以返回带有标记值的字符串。
var row CusipTableRow
row.GetColumnName(row.Id) //Return column name based on tag.
我正在考虑可能尝试使用 map[address] fieldTag,但可能由于没有完全掌握 unsafe 包,所以没有运气让它工作。如果这种方法行得通,我认为这样的调用可能行得通:
row.GetColumnName(&row.Id) //Return column name based on tag.
【问题讨论】:
你不能在一个字段上使用反射,它只是一个字段类型的值,来获取有关结构的信息。结构标签是结构类型信息的一部分。你可以有一个像GetColumnName(&row, "Id")
这样的函数,使用结构值和字段名,但是传递字段本身永远不会像你描述的那样工作。
@Adrian 是的,我同意你的观点,我只是不能在球场上使用反射。我的目标是避免在代码中出现像“Id”这样的硬编码字符串。有没有办法将地址传递给字段并引用对象的实例来实现这一点?
理论上你可以使用unsafe
和指针算法,但它会非常脆弱,并且潜在的错误将远远超过以这种方式调用函数而不是传递字段名称的设计偏好。您尝试根据 struct 字段获取数据库列名称的上下文是什么?通常,这发生在您使用反射遍历所有结构字段的情况下(这是大多数使用结构标签的库的工作方式),所以这应该是不必要的。
【参考方案1】:
给定结构的地址和字段的地址,您可以获得字段标记。不需要不安全的恶作剧。
func GetColumnName(pstruct interface, pfield interface) string
v := reflect.ValueOf(pstruct).Elem()
for i := 0; i < v.NumField(); i++
if v.Field(i).Addr().Interface() == pfield
return v.Type().Field(i).Tag.Get("db")
panic("field not in struct")
使用示例:
var v CusipTableRow
fmt.Println(GetColumnName(&v, &v.Added_Time)) // prints added_timestamp
Run it on the Go Playground.
【讨论】:
@I Love Reflection - 感谢代码,我正在稍微修改它并将其集成到我的数据模型中以访问数据库。在您看来,您是否觉得这是在 go 中使用反射的不好方法。也许更好的表达方式是有更好的方法来实现这一点,因为如果我想有一些单一的地方将字段映射到 db 列而不只是拥有过多的全局常量,那么 go 不允许使用结构常量。 我会编写一个从源代码生成字段名称常量的工具,例如const CusipTableRow_Added_Time = "added_timestamp"
。使用 go/parser 包将源解析为 AST,遍历 AST 以查找结构定义并生成常量。使用go generate 调用该工具。以上是关于反射以获取字段标记的主要内容,如果未能解决你的问题,请参考以下文章