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
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 metadata提取的PDF元数据,如何转换为一个Excel
  • ¥15 关于arduino编程toCharArray()函数的使用
  • ¥100 vc++混合CEF采用CLR方式编译报错
  • ¥15 coze 的插件输入飞书多维表格 app_token 后一直显示错误,如何解决?
  • ¥15 vite+vue3+plyr播放本地public文件夹下视频无法加载
  • ¥15 c#逐行读取txt文本,但是每一行里面数据之间空格数量不同
  • ¥50 如何openEuler 22.03上安装配置drbd
  • ¥20 ING91680C BLE5.3 芯片怎么实现串口收发数据
  • ¥15 无线连接树莓派,无法执行update,如何解决?(相关搜索:软件下载)
  • ¥15 Windows11, backspace, enter, space键失灵