问题描述
以下代码并不完全符合预期:
我假设我像往常一样搞砸了一些指针/引用的东西,但是我希望我的...
func unmarshalJSON(in [] byte, s接口{})错误
...和 encoding / json
$ pre $ func解组($ [$ byte $ v $ interface $ {$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ b
...的行为方式相同(例如更新引用传递的第二个参数)。
Long
上面的例子是最小的再现器,没有多大意义。这是为了使它在操场上工作。然而,一个不太简单的例子是有道理的:
package main
import(
FMT
的gopkg.in/yaml.v2
)
FUNC unmarshalYAML(在[]字节,s接口{})错误{
var result map [interface {}] interface {}
err:= yaml.Unmarshal(in,& result)$ b $ = cleanUpInterfaceMap(result)
// s是印刷按预期
fmt.Println(S)//地图[aoeu:[测试aoeu] oaeu:地图[mahl:aoec TASE:aoeu]]]
返回ERR
}
func cleanUpInterfaceArray(in [] interface {})[] interface {} {$ b $ out:= make([] interface {},len(in))
for i,v :=范围在{
out [i] = cleanUpMapValue(v)
}
返回
}
func cleanUpInterfaceMap(在map [interface { }] interface {}} map [string] interface {} {
out:= make(map [string] interface {})
for k,v:= range in {
out [ fmt.Sprintf(%v,k)] = cleanUpMapValue(v)
}
返回
}
func cleanUpMapValue(v interface {})interface {} {
switch v:= v。(type){
case [] interface { }:
return cleanUpInterfaceArray(v)
case map [interface {}] interface {}:
返回cleanUpInterfaceMap(v)
大小写字符串:
返回v
default:
return fmt.Sprintf(%v,v)
}
}
func main(){
s:= make (map [string] interface {})
b:= [] byte(`---
aoeu:
- test
- aoeu
oaeu:$ b $ !b - {TASE:aoeu,mahl:aoec}
')
错误:= unmarshalYAML(b,&安培; S)
如果ERR =零{
恐慌(ERR)
}
// s仍为空映射
fmt.Println(s)// map []
}
这个想法是将YAML解组为 map [string] interface {}
(而不是 map [interface {}] interface {}
)是为了允许序列化为JSON(其中标识符需要是字符串)。 unmarshalYAML
函数应该提供与 yaml.Unmarshal
...相同的函数声明...
使用类型断言
内的 unmarshalJSON()
函数参数 s
的行为就像一个局部变量。当你给它分配一些东西时:
s =结果
它只会改变局部变量的值。
既然你希望它能够改变一个 * map [string] interface {}
,这就是你传递给它的方法,你可以使用一个简单的来从中获取地图指针,并将该指针传递给 json.Unmarshal()
:
func unmarshalJSON(in [] byte,s interface {})error {
if m,ok:= s (*图[字符串]接口{}); !$ {
return errors.New(Expecting * map [string] interface {})
} else {
return json.Unmarshal(in,m)
}
}
试试你在。
只需传递
另外请注意,这是完全不必要的,因为也被定义为将目标作为类型 interface {}
的值,同样的事情你有。所以你甚至不需要做任何事情就传递它:
func unmarshalJSON(in [] byte,s interface { })error {
return json.Unmarshal(in,s)
}
在试试这个。
使用函数类型的变量
有趣的是, unmarshalJSON()的签名
和库函数 json.Unmarshal()
是相同的:
//你的:
func unmarshalJSON(in [] byte,s interface {})错误
// json包
func Unmarshal(data []字节,v接口{})错误
这意味着还有另一种选择,那就是你可以使用名为 unmarshalJSON
的变量,只是简单地分配函数值 json.Unmarshal
:
var unmarshalJSON func([] byte ,interface {})error = json.Unmarshal
现在你有一个变量 unmarshalJSON
,它是函数类型的,你可以像调用函数一样调用它:
err:= unmarshalJSON(b,& s)
在。
现在开始您的 unmarshalYAML()
函数
在 unmarshalYAML()
你也犯过同样的错误:
$ p $ s = cleanUpInterfaceMap(result)
这只会改变本地 s
变量(参数)的值,并且不会填充映射(指针)传递给 unmarshalYAML()
。
使用上面详述的类型断言技术从 s
interface {}
参数中获取指针,一旦拥有该指针,就可以更改指向对象(外部映射)。
func unmarshalYAML(in [] byte,s interface { )错误{
var dest * map [string] interface {}
var ok bool
如果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保存结果,dest是传递给我们的指针,
//我们可以设置指向的对象(map):
* dest = m
return nil
}
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/json
s...
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
...
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
}
这篇关于将值传递给接口{}的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!