将interface{}转换为在运行时计算的Type。

huangapple go评论71阅读模式
英文:

Casting interface{} to calculated in runtime Type

问题

这是我需要的示例原理图:

func decode(data []byte, target interface{}) (interface{}, error) {
    decoded := target.(reflect.TypeOf(target)) // 伪代码
    err := json.Unmarshal(data, &decoded)
    ...

我在 Stack Overflow 上找到了几个类似的问题,但所有的解决方案都是使用 target.(type) 进行切换。这不是我寻找的最佳解决方案。

我还找到了使用反射的解决方案:

decoded := reflect.ValueOf(target).Convert(reflect.TypeOf(target))
// fmt.Printf("%T", decoded) == reflect.Value

但我不知道如何从 reflect.Value 中获取结构体,以便将其传递给 json.Unmarshal 函数。

这个 decode 函数将如何使用?

我有多个不同结构的请求。我可以确定我应该使用哪个结构来解码请求。我有一个请求类型和结构之间的映射,类似于 map[RequestMethod]interface{}

示意版本如下:

func hydrate(data []byte) (interface{}, error) {
    var base baseResponse

    if err := json.Unmarshal(data, &base); err != nil {
        return nil, err
    }

    target, ok := methodsMap[Method(base.Method)]

    if !ok {
        return nil, errors.New("Trying to hydrate data with unknown method: " + base.Method)
    }

    decoded, err := decode(data, target) // 期望的目标类型。

    // 添加:

    如果我们将 `target` 传递给 `json.Unmarshal` 而不进行类型转换我们将得到 `map`示例

    ```go
    func decode(data []byte, target interface{}) (interface{}, error) {
        err := json.Unmarshal(data, &target)
        // fmt.Printf("%T", target) == map[string]interface{} 而不是结构体。
    ```

**解决方案**

感谢 [@icza](https://stackoverflow.com/users/1705598/icza),我们找到了解决方案:

```go
func hydrate(data []byte) (interface{}, error) {
    var base baseResponse

    if err := json.Unmarshal(data, &base); err != nil {
        return nil, err
    }

    target, ok := methodsMap[Method(base.Method)]

    if !ok {
        return nil, errors.New("Trying to hydrate data with unknown method: " + base.Method)
    }

    // 克隆请求草稿结构。
    decoded := reflect.New(reflect.ValueOf(target).Type()).Interface()
    err := decode(data, decoded)
    ...

相关链接:

英文:

Here is schematic example what I need:

func decode(data []byte, target interface{}) (interface{}, error) {
decoded := target.(reflect.TypeOf(target)) // pseudocode
err := json.Unmarshal(data, &decoded)
...

I found several similar questions on SO, but all solutions are about switch by target.(type). This is not the best solution what I look for.

Also I found solution with reflection:

decoded := reflect.ValueOf(target).Convert(reflect.TypeOf(target))
// fmt.Printf("%T", decoded) == reflect.Value

But I couldn't understand how to get struct from reflect.Value to pass it to json.Unmarshal function.

How this decode function will be used?

I have multiple requests in different structures. I can determine what struct I should use to decode request. I have mapping between request type and structure like this map[RequestMethod]interface{}.

Schematic version looks like this:

func hydrate(data []byte) (interface{}, error) {
var base baseResponse
if err := json.Unmarshal(data, &base); err != nil {
return nil, err
}
target, ok := methodsMap[Method(base.Method)]
if !ok {
return nil, errors.New("Trying to hydrate data with unknown method: " + base.Method)
}
decoded, err := decode(data, target) // Expected target type.

Added:

If we pass our target to json.Unmarshal without casting to it type we will obtain map. Example:

func decode(data []byte, target interface{}) (interface{}, error) {
err := json.Unmarshal(data, &target)
// fmt.Printf("%T", target) == map[string]interface{} not struct.

Solution

Thanks to @icza we found solution:

func hydrate(data []byte) (interface{}, error) {
var base baseResponse
if err := json.Unmarshal(data, &base); err != nil {
return nil, err
}
target, ok := methodsMap[Method(base.Method)]
if !ok {
return nil, errors.New("Trying to hydrate data with unknown method: " + base.Method)
}
// Clone request draft struct.
decoded := reflect.New(reflect.ValueOf(target).Type()).Interface()
err := decode(data, decoded)
...

Related links:

<https://stackoverflow.com/questions/38829941/golang-interface-type-misunderstanding/38830638#38830638>

<https://stackoverflow.com/questions/37851500/how-to-copy-an-interface-value-in-go/37851764#37851764>

答案1

得分: 5

使用以下代码创建draft struct的副本并解组到其中:

t := reflect.TypeOf(methodsMap[Method(base.Method)])
pv := reflect.New(t)
err := json.Unmarshal(p, pv.Interface())
return pv.Elem().Interface(), err  // pv.Elem()解引用指针

playground示例

英文:

Use the following code to create a copy of the draft struct and unmarshal to it:

t := reflect.TypeOf(methodsMap[Method(base.Method)])
pv := reflect.New(t)
err := json.Unmarshal(p, pv.Interface())
return pv.Elem().Interface(), err  // pv.Elem() dereferences ptr 

playground example

huangapple
  • 本文由 发表于 2016年11月30日 20:21:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/40888248.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定