英文:
Hold a type in a Golang struct field
问题
我正在寻找一种在自定义结构体中将类型(可能是reflection.Type)作为字段保存的方法。背后的原因是,我将JSON数组解码为结构体,然后构建SQL查询,但是在JSON数组中,整数、浮点数和时间戳是相同的,但在查询数据库时它们是不同的。这意味着在查询之前,我需要将每个值转换为其正确的类型。
我认为答案可能在reflect包中,但我还没有完全弄清楚如何使用它。
我希望的是像这样的东西:
type Column struct {
name string
dataType type
}
someColumn := Column {name: "somecol", dataType: int}
convertedVal := SomeConversionFunc("5", someColumn.dataType)
或者,也可以使用类似这样的方法:
type Column struct {
name string
dataType func()
}
someColumn := Column {name: "somecol", dataType: ConvertToInt}
convertedVal := someColumn.dataType("5")
有什么想法吗?
英文:
I'm looking for a way to hold a type (maybe reflection.Type?) as a field in a custom struct. The reasoning behind this is that I'm decoding JSON arrays into structs from which I later build SQL queries, but ints, floats and timestamps are identical in the JSON array, though they are different when querying a database. This means I need to convert each value to its correct type before querying.
I think the answer lies somewhere in the reflect package, but I haven't worked out exactly how to utilize it.
What I'm hoping for is something like this:
type Column struct {
name string
dataType type
}
someColumn := Column {name: "somecol", dataType: int}
convertedVal := SomeConversionFunc("5", someColumn.dataType)
Alternatively, this sort of thing could work as well:
type Column struct {
name string
dataType func()
}
someColumn := Column {name: "somecol", dataType: ConvertToInt}
convertedVal := someColumn.dataType("5")
Any ideas?
答案1
得分: 5
你的代码看起来是在Column
结构体中使用了reflect.Type
。你可以通过import "reflect"
来获取它。
如果你的Column
结构体包含类型名称和值(作为原始字符串),你应该能够编写一个方法,根据类型产生正确类型的值。在这里可以找到关于switch语句的简要参考:https://golang.org/doc/effective_go.html#switch
type Column struct {
Name string
DataType reflect.Type
TypedValue interface{}
StringValue string
}
因为你说“但是在JSON数组中,整数、浮点数和时间戳是相同的”,我假设你的JSON中的所有值在技术上都是字符串(即它们都带引号)。你可能不需要所有这些字段,但是想法是将类型信息与属性名称和原始值关联起来。如果你使用类型为interface{}
的字段,你可以将任何值分配给它,因此你可以在这个结构体的实例中很容易地保存原始的JSON数据(名称和值),以及类型信息和值。
英文:
You're on the right track I suppose. In your Column
struct you're looking for reflect.Type
. Which you'll get with import "reflect"
.
If your column struct contains the type name and value (as a raw string) you should be able to write method that switches on type and produces a value of the correct type for each case. Brief reference for switches here; https://golang.org/doc/effective_go.html#switch
type Column struct {
Name string
DataType reflect.Type
TypedValue interface{}
StringValue string
}
Because you said "but ints, floats and timestamps are identical in the JSON array" I'm assuming all the values in your json are technically strings (meaning they're all quoted). You may not need all those fields but the idea is to have the type information coupled with the properties name and the original value. If you use the field of type interface{}
you can assign anything to it so you could hold the original json data (name and value) as well as the type information and the value in a sensible Go type in an instance of this struct pretty easily.
答案2
得分: 2
我尝试使用@evanmcdonnal提供的解决方案,但我无法找到一种通用的方法来将float64
(这是json.Unmarshal
从JSON数组中解组的任何数字的类型)转换为数据库中的任何数据类型(timestamp
证明有点棘手,因为reflect.Value
没有导出到time.Time
的转换方法,而time.Time
是Cassandra的timestamp
的等效类型)。
有效的方法是使用typeConversion
字段而不是dataType
字段,即保存一个将变量类型从json.Unmarshal
设置的类型转换为目标列类型的函数。
因此,我的结构如下所示:
type Column struct {
name string
typeConversion func(reflect.Value) reflect.Value
}
我已经有一些typeConversion
函数,类似于以下示例:
func floatToTime(varFloat reflect.Value) reflect.Value {
timestamp := time.Unix(int64(varFloat.Float()), 0)
return reflect.ValueOf(timestamp)
}
func floatToInt(varFloat reflect.Value) reflect.Value {
return reflect.ValueOf(int(varFloat.Float()))
}
这实际上是一个非常好的解决方案,因为它非常通用:结构定义了任何转换函数应该如何构建,这意味着我可以包装任何形式的转换以适应此API,并且由于返回值始终是reflect.Value
,我可以通过调用Interface()
来访问具有正确类型的底层值,例如:
// value是由json解组的数据
// 我将其转换为reflect.Value,然后再次使用自定义的typeConversion进行转换
// 然后,我使用Interface()获取最终值,以最终(适用于列的)类型
column.typeConversion(reflect.ValueOf(value)).Interface()
英文:
I attempted to use the solution offered by @evanmcdonnal but I could not find a generic way of converting float64
(which is the type json.Unmarshal
gives to any number it unmarshals from the json array) to any data type found in the DB (timestamp
proved to be a bit tricky, because reflect.Value
does not export a conversion method to time.Time
, which is the equivalent of Cassandra's timestamp
).
What did work was using a typeConversion
field instead of a dataType
field, that is, holding a function that converts to the distined column's type from the type json.Unmarshal
sets the variable's type to.
My struct, therefore, looks like this:
type Column struct {
name string
typeConversion func(reflect.Value) reflect.Value
}
Some typeConversion functions I already have look like this:
func floatToTime(varFloat reflect.Value) reflect.Value {
timestamp := time.Unix(int64(varFloat.Float()), 0)
return reflect.ValueOf(timestamp)
}
func floatToInt(varFloat reflect.Value) reflect.Value {
return reflect.ValueOf(int(varFloat.Float()))
}
This actually happens to be a very good solution, because it is extremely generic: the struct defines the way any conversion function should be built, which means I can wrap any form of conversion to fit this API, and because the return value is always reflect.Value
, I can access the underlying value with the correct type by calling Interface()
, like so:
// value is the data unmarshaled by json
// I convert it to reflect.Value, then convert it again using my custom typeConversion
// Then I use Interface() to get the final value in the final (and column-appropriate) type
column.typeConversion(reflect.ValueOf(value)).Interface()
答案3
得分: 0
StructTag应该会有所帮助:
type Table struct {
age int `sql:"type:int;`
price float `sql:"type:float;`
}
英文:
The StructTag should help:
type Table struct {
age int `sql:"type:int;`
price float `sql:"type:float;`
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论