短
以下代码不能完全满足您的期望:https : //play.golang.org/p/sO4w4I_Lle
我以为我像往常一样弄乱了一些指针/引用的东西,但是我希望我的…
func unmarshalJSON(in []byte, s interface{}) error
…和encoding/json…
encoding/json
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。
map[string]interface{}
map[interface{}]interface{}
unmarshalYAML
yaml.Unmarshal
在unmarshalJSON()函数内部,参数的s行为类似于局部变量。当您分配一些东西给它时:
unmarshalJSON()
s
s = result
它只会更改局部变量的值。
由于您希望它与更改a的值一起使用*map[string]interface{},这就是您要传递给它的内容,因此可以使用简单的类型断言从中获取映射指针,并将此指针传递给json.Unmarshal():
*map[string]interface{}
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()将目的地也定义为type的值interface{}也就是您所拥有的。因此,您甚至不必执行任何操作即可:
interface{}
func unmarshalJSON(in []byte, s interface{}) error { return json.Unmarshal(in, s) }
在Go Playground上尝试一下。
有趣的是,您和库函数的 签名 是相同的:unmarshalJSON()``json.Unmarshal()
unmarshalJSON()``json.Unmarshal()
// Yours: func unmarshalJSON(in []byte, s interface{}) error // json package func Unmarshal(data []byte, v interface{}) error
这意味着还有另外一个选择,那就是你可以使用一个命名的变量unmarshalJSONa的函数类型,并只分配函数值json.Unmarshal:
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{}参数获取指针,一旦有了指针,就可以更改 指向的 对象(“外部”映射)。
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 }