dongzhucha3999 2016-02-17 06:31
浏览 179
已采纳

将值传递给接口{}

Short

The following code does not exactly do what expected: https://play.golang.org/p/sO4w4I_Lle

I assume that I mess up some pointer/reference stuff as usual, however I expect my...

func unmarshalJSON(in []byte, s interface{}) error

... and encoding/jsons...

func Unmarshal(data []byte, v interface{}) error 

...to behave the same way (eg. update the referenced passed as second argument).

Long

The example above is a minimal reproducer that does not make much sense. This is in order to make it work on the playground. However, an less minimal example that does make sense is this:

package main

import (
    "fmt"

    "gopkg.in/yaml.v2"
)

func unmarshalYAML(in []byte, s interface{}) error {
    var result map[interface{}]interface{}
    err := yaml.Unmarshal(in, &result)
    s = cleanUpInterfaceMap(result)
    // s is printed as expected
    fmt.Println(s) // map[aoeu:[test aoeu] oaeu:[map[mahl:aoec tase:aoeu]]]
    return err
}

func cleanUpInterfaceArray(in []interface{}) []interface{} {
    out := make([]interface{}, len(in))
    for i, v := range in {
        out[i] = cleanUpMapValue(v)
    }
    return out
}

func cleanUpInterfaceMap(in map[interface{}]interface{}) map[string]interface{} {
    out := make(map[string]interface{})
    for k, v := range in {
        out[fmt.Sprintf("%v", k)] = cleanUpMapValue(v)
    }
    return out
}

func cleanUpMapValue(v interface{}) interface{} {
    switch v := v.(type) {
    case []interface{}:
        return cleanUpInterfaceArray(v)
    case map[interface{}]interface{}:
        return cleanUpInterfaceMap(v)
    case string:
        return v
    default:
        return fmt.Sprintf("%v", v)
    }
}

func main() {
    s := make(map[string]interface{})
    b := []byte(`---
aoeu:
- test
- aoeu
oaeu:
- { tase: aoeu, mahl: aoec}
`)
    err := unmarshalYAML(b, &s)
    if err != nil {
        panic(err)
    }
    // s is still an empty map
    fmt.Println(s) // map[]
}

The idea is to unmarshal YAML to map[string]interface{} (instead of map[interface{}]interface{}) is order to allow to serialize to JSON (where identifiers need to be strings). The unmarshalYAML function should provide the same func signture as yaml.Unmarshal...

  • 写回答

1条回答 默认 最新

  • douren6035 2016-02-17 06:43
    关注

    Using Type assertion

    Inside your unmarshalJSON() function the parameter s behaves like a local variable. When you assign something to it:

    s = result
    

    It will only change the value of the local variable.

    Since you want it to work with changing the value of a *map[string]interface{} and that is what you pass to it, you could use a simple type assertion to obtain the map pointer from it, and pass this pointer to json.Unmarshal():

    func unmarshalJSON(in []byte, s interface{}) error {
        if m, ok := s.(*map[string]interface{}); !ok {
            return errors.New("Expecting *map[string]interface{}")
        } else {
            return json.Unmarshal(in, m)
        }
    }
    

    Try your modified, working example on the Go Playground.

    Just passing it along

    Also note that however this is completely unnecessary as json.Unmarshal() is also defined to take the destination as a value of type interface{}, the same thing you have. So you don't even have to do anything just pass it along:

    func unmarshalJSON(in []byte, s interface{}) error {
        return json.Unmarshal(in, s)
    }
    

    Try this on the Go Playground.

    With a variable of function type

    As an interesting thing note that the signature of your unmarshalJSON() and the library function json.Unmarshal() is identical:

    // Yours:
    func unmarshalJSON(in []byte, s interface{}) error
    
    // json package
    func Unmarshal(data []byte, v interface{}) error
    

    This means there is another option, that is you could use a variable named unmarshalJSON of a function type, and just simply assign the function value json.Unmarshal:

    var unmarshalJSON func([]byte, interface{}) error = json.Unmarshal
    

    Now you have a variable unmarshalJSON which is of function type, and you can call it as if it would be a function:

    err := unmarshalJSON(b, &s)
    

    Try this function value on the Go Playground.

    Now on to your unmarshalYAML() function

    In your unmarshalYAML() you do the same mistake:

    s = cleanUpInterfaceMap(result)
    

    This will only change the value of your local s variable (parameter), and it will not "populate" the map (pointer) passed to unmarshalYAML().

    Use the type assertion technique detailed above to obtain the pointer from the s interface{} argument, and once you have that, you can change the pointed object (the "outside" map).

    func unmarshalYAML(in []byte, s interface{}) error {
        var dest *map[string]interface{}
        var ok bool
        if dest, ok = s.(*map[string]interface{}); !ok {
            return errors.New("Expecting *map[string]interface{}")
        }
    
        var result map[interface{}]interface{}
        if err := yaml.Unmarshal(in, &result); err != nil {
            return err
        }
        m := cleanUpInterfaceMap(result)
    
        // m holds the results, dest is the pointer that was passed to us,
        // we can just set the pointed object (map):
        *dest = m
        return nil
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 Html备忘录页面制作
  • ¥15 黄永刚的晶体塑性子程序中输入的材料参数里的晶体取向参数是什么形式的?
  • ¥20 数学建模来解决我这个问题
  • ¥15 计算机网络ip分片偏移量计算头部是-20还是-40呀
  • ¥15 stc15f2k60s2单片机关于流水灯,时钟,定时器,矩阵键盘等方面的综合问题
  • ¥15 YOLOv8已有一个初步的检测模型,想利用这个模型对新的图片进行自动标注,生成labellmg可以识别的数据,再手动修改。如何操作?
  • ¥30 NIRfast软件使用指导
  • ¥20 matlab仿真问题,求功率谱密度
  • ¥15 求micropython modbus-RTU 从机的代码或库?
  • ¥15 django5安装失败