将值传递给接口{} [英] Passing values to interface{}

查看:156
本文介绍了将值传递给接口{}的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



以下代码并不完全符合预期:
https://play.golang.org/p/sO4w4I_Lle



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

  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 {} ,这就是你传递给它的方法,你可以使用一个简单的 type assertion 来从中获取地图指针,并将该指针传递给 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)
}
}

试试你在 Go Goground



只需传递



另外请注意,这是完全不必要的,因为 json.Unmarshal() 也被定义为将目标作为类型 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)

Go Playground



现在开始您的 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/jsons...

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
}

这篇关于将值传递给接口{}的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆