pointers - 将值传递给接口(interface){}

标签 pointers go interface yaml unmarshalling

以下代码并没有完全按照预期执行: https://play.golang.org/p/sO4w4I_Lle

我假设我像往常一样弄乱了一些指针/引用的东西,但是我希望我的...

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

...和encoding/json...

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

...以相同的方式运行(例如,更新作为第二个参数传递的引用)。

上面的例子是一个没有多大意义的最小复制器。这是为了让它在 Playground 上工作。然而,一个不太有意义的例子是这样的:

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[]
}

想法是将 YAML 解码为 map[string]interface{}(而不是 map[interface{}]interface{})以允许序列化到 JSON(其中标识符需要是字符串)。 unmarshalYAML 函数应提供与 yaml.Unmarshal 相同的函数签名...

最佳答案

使用类型断言

在您的 unmarshalJSON() 函数中,参数 s 的行为类似于局部变量。当你给它赋值时:

s = result

它只会改变局部变量的值。

由于您希望它与更改 *map[string]interface{} 的值一起工作,而这就是您传递给它的内容,您可以使用简单的 type assertion从中获取 map 指针,并将此指针传递给 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)
    }
}

Go Playground 上尝试您修改后的工作示例.

只是传递它

另请注意,这完全没有必要,因为 json.Unmarshal()也被定义为将目的地作为 interface{} 类型的值,这与您拥有的相同。所以你甚至不需要做任何事情,只需传递它:

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

Go Playground 上试试这个.

带有函数类型的变量

有趣的是,您的 unmarshalJSON() 和库函数 json.Unmarshal()签名是相同的:

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

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

这意味着还有另一种选择,即您可以使用 function type 的名为 unmarshalJSON 的变量, 只需简单地分配函数值 json.Unmarshal:

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

现在你有了一个函数类型的变量unmarshalJSON,你可以像调用函数一样调用它:

err := unmarshalJSON(b, &s)

Go Playground 上尝试这个函数值.

现在开始你的 unmarshalYAML() 函数

在您的 unmarshalYAML() 中,您犯了同样的错误:

s = cleanUpInterfaceMap(result)

这只会更改您的本地 s 变量(参数)的值,它不会“填充”传递给 unmarshalYAML() 的映射(指针)。

使用上面详述的类型断言技术从 s interface{} 参数中获取指针,一旦获得该指针,您就可以更改 pointed 对象(“外部” 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
}

关于pointers - 将值传递给接口(interface){},我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35449466/

相关文章:

http - Golang根据struct字段设置http响应码

c++ - 在代码::Blocks中更改类变量大小写

c# - 减少 NUnit 测试中接口(interface)不同实现的重复

c++ - 取消引用指针以获取值或创建本地对象并以这种方式访问​​值更好吗?

c++ - 使用指针变量打印特定的数组变量

C++,在for循环中分配空间,可能的内存泄漏验证

c - *y++ 和++*y 的区别?

go - 需要帮助在 GoLang 中读/写 ECDSA key

templates - {{template "base"}} 和 {{template "base".}} 在 go-gin 中的区别

java - Java中如何用接口(interface)替换抽象类?