在 Golang 结构字段中保存类型

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")

有什么想法吗?

我想你走在正确的轨道上。在您的 Column 结构中,您正在寻找 reflect.Type。你会得到 import "reflect".

如果您的列结构包含类型名称和值(作为原始字符串),您应该能够编写方法来切换类型并为每种情况生成正确类型的值。此处开关的简要参考; https://golang.org/doc/effective_go.html#switch

type Column struct {
    Name string
    DataType reflect.Type
    TypedValue interface{}
    StringValue string
}

因为你说 "but ints, floats and timestamps are identical in the JSON array" 我假设你 json 中的所有值在技术上都是字符串(意味着它们都被引用)。您可能不需要所有这些字段,但想法是将类型信息与属性名称和原始值相结合。如果你使用类型 interface{} 的字段,你可以给它赋值任何东西,这样你就可以保存原始的 json 数据(名称和值)以及类型信息和合理的 Go 类型中的值这个结构的一个实例很容易。

我尝试使用@evanmcdonnal 提供的解决方案,但我找不到转换 float64 的通用方法(这是 json.Unmarshal 赋予它从 json array) 到 DB 中找到的任何数据类型(timestamp 被证明有点棘手,因为 reflect.Value 不会导出转换方法到 time.Time,这相当于卡桑德拉的 timestamp).

所做的工作是使用 typeConversion 字段而不是 dataType 字段,也就是说,持有一个函数,该函数从类型 json.Unmarshal 设置转换为区分列的类型变量类型为.

因此,我的结构如下所示:

type Column struct {
    name string
    typeConversion func(reflect.Value) reflect.Value
}

我已经拥有的一些类型转换函数如下所示:

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 ,并且因为 return 值始终是 reflect.Value,我可以通过调用 Interface() 来访问具有正确类型的基础值,如下所示:

// 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()

StructTag 应该有帮助:

type Table struct {
    age int `sql:"type:int;`
    price float `sql:"type:float;`
}