为了账号安全,请及时绑定邮箱和手机立即绑定

如何检查声明为map[string]interface{}的变量实际上是mapstring?

如何检查声明为map[string]interface{}的变量实际上是mapstring?

Go
aluckdog 2023-07-31 16:47:36
我有一个变量需要是 astring或map[string]string(将从 JSON 反序列化)。所以我将其声明为interface{}. 我如何检查该值是否为map[string]string?仅在变量声明为map[string]stringnot 且变量为 的情况下才有效interface{}。package mainimport (    "fmt")func main() {    var myMap interface{}    myMap = map[string]interface{}{        "foo": "bar",    }    _, ok := myMap.(map[string]string)    if !ok {        fmt.Println("This will be printed")    }}请参阅https://play.golang.org/p/mA-CVk7bdb9不过我可以使用两种类型断言。一张在地图上,一张在地图上的值。package mainimport (    "fmt")func main() {    var myMap interface{}    myMap = map[string]interface{}{        "foo": "bar",    }    valueMap, ok := myMap.(map[string]interface{})    if !ok {        fmt.Println("will not be printed")    }    for _, v := range valueMap {        if _, ok := v.(string); !ok {            fmt.Println("will not be printed")        }    }}问:有更好的办法吗?
查看完整描述

1 回答

?
慕容708150

TA贡献1831条经验 获得超4个赞

如果将变量声明为 type interface{},则它就是type interface{}。它永远不具有map[keytype]valuetype某种价值。但是类型变量interface{}可以保存具有其他具体类型的值。当它这样做时,它就这样做——仅此而已。它仍然type interface{},但它包含其他类型的值。

接口值由两部分组成

这里的关键区别在于interface{}变量什么以及它包含什么。任何接口变量实际上都有两个槽:一个用于保存其中存储的类型,另一个用于保存其中存储的值。每当您(或任何人)为变量赋值时,编译器都会填充两个槽:类型(来自您使用的值的类型)和值(来自您使用的值)。1nil如果接口变量在两个槽中 都有,则比较等于 nil ;这也是默认的零值。

因此,您的运行时测试:

valueMap, ok := myMap.(map[string]interface{})

是明智的做法:如果myMap持有一个类型为 的值map[string]interfaceok则将其设置为truevalueMap包含该值(具有该类型)。如果myMap持有某个其他类型的值,ok则设置为falsevalueMap设置为 类型的零值map[string]interface{}。换句话说,在运行时,代码首先检查类型槽,然后将值槽复制到valueMap并设置ok为 true,或者设置valueMapnil并设置ok为 false。

如果且当ok已设置为时true,每个valueMap[k]都是type interface{}。和以前一样,对于myMap其本身,每个interface{}变量都可以(但不必)保存 type 的值string,并且您必须使用某种“实际类型和值是什么”运行时测试来区分它们。

当您将json.Unmarshal解码后的 JSON 填充到 类型的变量中时interface{},它能够反序列化任何这些记录的 JSON 类型。然后该列表告诉您什么类型被填充到接口变量中:

bool, for JSON booleans

float64, for JSON numbers

string, for JSON strings

[]interface{}, for JSON arrays

map[string]interface{}, for JSON objects

nil for JSON null

因此,在执行json.Unmarshal完类型为 的变量之后interface{},您应该检查变量的类型槽中放入的类型。您可以使用断言和ok布尔值来完成此操作,或者如果您愿意,也可以使用类型开关对其进行解码:


var i interface

if err := json.Unmarshal(data, &i); err != nil {

    panic(err)

}

switch v := i.(type) {

case string:

    ... code ...

case map[string]interface{}:

    ... code ...

... add some or all of the types listed ...

}

问题是,无论您在此处的代码中执行什么操作,您都确实已将某些json.Unmarshal内容放入interface{}, 并且interface{} 是的类型i。您必须在运行时测试接口保存的类型和值对。


您的另一个选择是手动检查 JSON 字符串并决定提供给 的变量类型json.Unmarshal。这使得您可以在后面编写更少的代码,但在 之前Unmarshal编写更多代码。


在 Go Playground 上有一个更完整的示例,使用类型开关来检查json.Unmarshal. 它故意不完整,但我认为,有足够的输入和输出案例,可以让您了解如何处理所有内容,考虑到上面关于写入类型变量的内容的json.Unmarshal引用interface{}。


1当然,如果您interface{}从其他地方分配一个interface{}:


var i1, i2 interface{}

... set i1 from some actual value ...

// more code, then:

i2 = i1

编译器只是将两个槽从 复制i1到i2。当你这样做时,两个独立插槽的事情就会变得更加清晰:


var f float64

... code that sets f to, say, 1.5 ...

i2 = f

例如,当写入float64类型槽时,将值1.5写入值槽中。编译器知道这f一点,float64因此类型设置只是意味着“在其中粘贴一个常量”。编译器不一定知道的值f,因此值设置是实际值的副本。


查看完整回答
反对 回复 2023-07-31
  • 1 回答
  • 0 关注
  • 131 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信