Pointers 将值传递给接口{}

Pointers 将值传递给接口{},pointers,go,interface,yaml,unmarshalling,Pointers,Go,Interface,Yaml,Unmarshalling,短期 以下代码不完全符合预期: 我假设我像往常一样搞砸了一些指针/引用的东西,但是我希望我的 func unmarshalJSON(in []byte, s interface{}) error 。。。和encoding/jsons func Unmarshal(data []byte, v interface{}) error …以相同的方式进行操作(例如,更新作为第二个参数传递的引用) Long 上面的例子是一个没有多大意义的最小复制器。这是为了让它在操场上发挥作用。然而,一个不太简

短期

以下代码不完全符合预期:

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

func unmarshalJSON(in []byte, s interface{}) error
。。。和
encoding/json
s

func Unmarshal(data []byte, v interface{}) error 
…以相同的方式进行操作(例如,更新作为第二个参数传递的引用)

Long

上面的例子是一个没有多大意义的最小复制器。这是为了让它在操场上发挥作用。然而,一个不太简单的例子是:

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]接口{}
的值,而这正是您传递给它的内容,因此您可以使用一个简单的方法从它获取映射指针,并将此指针传递给
json.Unmarshal()

在上尝试修改后的工作示例

只是把它传过去 另外请注意,这是完全不必要的,因为它也定义为将目的地作为
接口{}
类型的值,这与您的定义相同。所以你甚至不必做任何事情,只需传递:

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

使用函数类型的变量 有趣的是,请注意
unmarshalJSON()
的签名和库函数
json.Unmarshal()
是相同的:

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

// json package
func Unmarshal(data []byte, v interface{}) error
这意味着还有另一个选项,即可以使用a的名为
unmarshalJSON
的变量,只需分配函数值
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)
    }
}
var unmarshalJSON func([]byte, interface{}) error = json.Unmarshal
现在您有了一个函数类型的变量
unmarshalJSON
,您可以像调用函数一样调用它:

err := unmarshalJSON(b, &s)
在上尝试此函数值

现在转到
解组halyaml()
函数 在
unmarshalyml()
中,您也犯了同样的错误:

s = cleanUpInterfaceMap(result)
这只会更改本地
s
变量(参数)的值,不会“填充”传递到
unmarshalyml()的映射(指针)。

使用上面详述的类型断言技术从
s
接口{}
参数获取指针,一旦获得了指针,就可以更改指向的对象(“外部”映射)

使用类型断言 在
unmarshalJSON()
函数中,参数
s
的行为类似于局部变量。当您为其分配某些内容时:

s = result
它只会更改局部变量的值

由于您希望它能够更改
*map[string]接口{}
的值,而这正是您传递给它的内容,因此您可以使用一个简单的方法从它获取映射指针,并将此指针传递给
json.Unmarshal()

在上尝试修改后的工作示例

只是把它传过去 另外请注意,这是完全不必要的,因为它也定义为将目的地作为
接口{}
类型的值,这与您的定义相同。所以你甚至不必做任何事情,只需传递:

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

使用函数类型的变量 有趣的是,请注意
unmarshalJSON()
的签名和库函数
json.Unmarshal()
是相同的:

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

// json package
func Unmarshal(data []byte, v interface{}) error
这意味着还有另一个选项,即可以使用a的名为
unmarshalJSON
的变量,只需分配函数值
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)
    }
}
var unmarshalJSON func([]byte, interface{}) error = json.Unmarshal
现在您有了一个函数类型的变量
unmarshalJSON
,您可以像调用函数一样调用它:

err := unmarshalJSON(b, &s)
在上尝试此函数值

现在转到
解组halyaml()
函数 在
unmarshalyml()
中,您也犯了同样的错误:

s = cleanUpInterfaceMap(result)
这只会更改本地
s
变量(参数)的值,不会“填充”传递到
unmarshalyml()的映射(指针)。

使用上面详述的类型断言技术从
s
接口{}
参数获取指针,一旦获得了指针,就可以更改指向的对象(“外部”映射)


谢谢@icza!您介意看一下我提供的“长”示例吗?我理解你所指出的,但还没有找到一种方法将其应用于unmarshalYAML()。@sontags但这正是发生的事情。或多或少在上面的示例中,我没有检查是否传递了map或
nil
map值,它只会“覆盖”传递的map变量。但是它从外部是可见的。@sontags另外,您可以检查
*dest
是否不是
nil
,如果不是,您可以向该映射添加(复制)值,但更清楚的是,只需赋值,因为该映射可能已经包含值,从而进一步混淆这些元素的来源(例如,我们已经在地图中,或者是解组的结果)。很好!我花了很长时间才写下我的评论,同时你更新了你的答案!非常感谢!谢谢@icza!你介意看看我提供的“long”示例吗?我理解你指的是什么,但还没有找到将此应用于解组的方法()然而…@sontags但这正是发生的事情。或多或少。在上面的示例中,我不检查是否传递了map或
nil
map值,它只会“覆盖”传递的map变量。但它将从外部可见。@sontags另外,您可以检查
*dest