将值传递给接口{}
Passing values to interface{}
短
以下代码没有完全按照预期执行:
https://play.golang.org/p/sO4w4I_Lle
我假设我像往常一样弄乱了一些 pointer/reference 东西,但是我希望我的...
func unmarshalJSON(in []byte, s interface{}) error
... 和 encoding/json
s...
func Unmarshal(data []byte, v interface{}) error
...以相同的方式运行(例如,更新作为第二个参数传递的引用)。
长
上面的例子是一个没有多大意义的最小复制器。这是为了让它在操场上工作。然而,一个不太有意义的例子是这样的:
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 从中获取地图指针,并且将此指针传递给 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)
}
}
上尝试您修改后的工作示例
顺便传递一下
另请注意,这完全没有必要,因为 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
变量(参数)的值,而不会"populate"传递给unmarshalYAML()
的地图(指针)。
使用上面详述的类型断言技术从 s
interface{}
参数中获取指针,一旦有了,就可以更改 pointed 对象("outside" 地图)。
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
}
短
以下代码没有完全按照预期执行: https://play.golang.org/p/sO4w4I_Lle
我假设我像往常一样弄乱了一些 pointer/reference 东西,但是我希望我的...
func unmarshalJSON(in []byte, s interface{}) error
... 和 encoding/json
s...
func Unmarshal(data []byte, v interface{}) error
...以相同的方式运行(例如,更新作为第二个参数传递的引用)。
长
上面的例子是一个没有多大意义的最小复制器。这是为了让它在操场上工作。然而,一个不太有意义的例子是这样的:
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 从中获取地图指针,并且将此指针传递给 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)
}
}
上尝试您修改后的工作示例
顺便传递一下
另请注意,这完全没有必要,因为 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
变量(参数)的值,而不会"populate"传递给unmarshalYAML()
的地图(指针)。
使用上面详述的类型断言技术从 s
interface{}
参数中获取指针,一旦有了,就可以更改 pointed 对象("outside" 地图)。
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
}