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

我有一个变量需要是 astringmap[string]string(将从 JSON 反序列化)。所以我将其声明为interface{}. 我如何检查该值是否为map[string]string?仅在变量声明为map[string]stringnot 且变量为 的情况下才有效interface{}

package main


import (

    "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 main


import (

    "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")

        }

    }

}


问:有更好的办法吗?


aluckdog
浏览 145回答 1
1回答

慕容708150

如果将变量声明为 type interface{},则它就是type interface{}。它永远不具有map[keytype]valuetype某种价值。但是类型变量interface{}可以保存具有其他具体类型的值。当它这样做时,它就这样做——仅此而已。它仍然是type interface{},但它包含其他类型的值。接口值由两部分组成这里的关键区别在于interface{}变量是什么以及它包含什么。任何接口变量实际上都有两个槽:一个用于保存其中存储的类型,另一个用于保存其中存储的值。每当您(或任何人)为变量赋值时,编译器都会填充两个槽:类型(来自您使用的值的类型)和值(来自您使用的值)。1nil如果接口变量在两个槽中 都有,则比较等于 nil ;这也是默认的零值。因此,您的运行时测试:valueMap, ok := myMap.(map[string]interface{})是明智的做法:如果myMap持有一个类型为 的值map[string]interface,ok则将其设置为true并valueMap包含该值(具有该类型)。如果myMap持有某个其他类型的值,ok则设置为false并valueMap设置为 类型的零值map[string]interface{}。换句话说,在运行时,代码首先检查类型槽,然后将值槽复制到valueMap并设置ok为 true,或者设置valueMap为nil并设置ok为 false。如果且当ok已设置为时true,每个valueMap[k]值都是type interface{}。和以前一样,对于myMap其本身,每个interface{}变量都可以(但不必)保存 type 的值string,并且您必须使用某种“实际类型和值是什么”运行时测试来区分它们。当您将json.Unmarshal解码后的 JSON 填充到 类型的变量中时interface{},它能够反序列化任何这些记录的 JSON 类型。然后该列表告诉您什么类型被填充到接口变量中:bool, for JSON booleansfloat64, for JSON numbersstring, for JSON strings[]interface{}, for JSON arraysmap[string]interface{}, for JSON objectsnil for JSON null因此,在执行json.Unmarshal完类型为 的变量之后interface{},您应该检查变量的类型槽中放入的类型。您可以使用断言和ok布尔值来完成此操作,或者如果您愿意,也可以使用类型开关对其进行解码:var i interfaceif 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,因此值设置是实际值的副本。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go