简体   繁体   English

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

[英]How to check variable declared as map[string]interface{} is actually map[string]string?

I have a variable that needs to be either a string or map[string]string (will be deserializing from JSON).我有一个变量需要是stringmap[string]string (将从 JSON 反序列化)。 So I declare it as interface{} .所以我将它声明为interface{} How can I check that the value is map[string]string ?如何检查该值是否为map[string]string

This question How to check interface is a map[string]string in golang almost answers my question.这个问题How to check interface is a map[string]string in golang几乎回答了我的问题。 But the accepted answer only works if the variable is declared as a map[string]string not if the variable is interface{} .但是只有当变量被声明为map[string]string时,接受的答案才有效,如果变量是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")
    }
}

See https://play.golang.org/p/mA-CVk7bdb9https://play.golang.org/p/mA-CVk7bdb9

I can use two type assertions though.我可以使用两种类型的断言。 One on the map and one on the map value.一个在 map 上,一个在 map 值上。

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

See https://play.golang.org/p/hCl8eBcKSqEhttps://play.golang.org/p/hCl8eBcKSqE

Question: is there a better way?问:有没有更好的方法?

If you declare a variable as type interface{} , it is type interface{} .如果将变量声明为interface{}类型,则它interface{}类型。 It is not , ever, some map[keytype]valuetype value.它永远不是某个map[keytype]valuetype值。 But a variable of type interface{} can hold a value that has some other concrete type.但是interface{}类型的变量可以保存具有其他具体类型的值。 When it does so, it does so—that's all there is to it.当它这样做时,它就这样做了——这就是它的全部。 It still is type interface{} , but it holds a value of some other type.它仍然interface{}类型,但它拥有其他类型的值。

An interface value has two parts一个接口值有两个部分

The key distinction here is between what an interface{} variable is , and what it holds .这里的关键区别在于interface{}变量什么,以及它所持有的。 Any interface variable actually has two slots inside it: one to hold what type is stored in it, and one to hold what value is stored in it.任何接口变量实际上都有两个槽:一个用于保存其中存储的类型,另一个用于保存其中存储的 Any time you—or anyone—assign a value to the variable, the compiler fills in both slots: the type, from the type of the value you used, and the value, from the value you used.每当您或任何人为变量赋值,编译器都会填充两个槽:类型,来自您使用的值的类型,以及值,来自您使用的值。 1 The interface variable compares equal to nil if it has nil in both slots; 1如果接口变量在两个槽中都为nil ,则接口变量比较等于 nil; and that's also the default zero value.这也是默认的零值。

Hence, your runtime test:因此,您的运行时测试:

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

is a sensible thing to do: if myMap holds a value that has type map[string]interface , ok gets set to true and valueMap contains the value (which has that type).明智的做法是:如果myMap包含一个类型为map[string]interface的值,则ok设置为true并且valueMap包含该值(具有该类型)。 If myMap holds a value with some other type, ok gets set to false and valueMap gets set to the zero-value of type map[string]interface{} .如果myMap持有其他类型的值,则ok设置为false并且valueMap设置为map[string]interface{}类型的零值。 In other words, at runtime, the code checks the type-slot first, then either copies the value-slot across to valueMap and sets ok to true, or sets valueMap to nil and sets ok to false.换句话说,在运行时,代码首先检查类型槽,然后将值槽复制到valueMap并将ok设置为 true,或者将valueMap设置为nil并将ok设置为 false。

If and when ok has been set to true , each valueMap[k] value is type interface{} .如果并且当ok设置为true时,每个valueMap[k]都是类型interface{} As before, for myMap itself, each of these interface{} variables can—but do not have to—hold a value of type string , and you must use some sort of "what is the actual type-and-value" run-time test to tease them apart.和以前一样,对于myMap本身,这些interface{}变量中的每一个都可以(但不必)保存string类型的值,并且您必须使用某种“什么是实际的类型和值”运行时测试将它们分开。

When you use json.Unmarshal to stuff decoded JSON into a variable of type interface{} , it is capable of deserializing any of these documented JSON types.当您使用json.Unmarshal将解码的 JSON 填充到interface{}类型的变量中时,它能够反序列化任何这些记录在案的 Z0ECD11C1D7A287401D148A23BBD7A2F8 类型。 The list then tells you what type gets stuffed into the interface variable:然后该列表会告诉您将什么类型填充到接口变量中:

 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

So after doing json.Unmarshal into a variable of type interface{} , you should check what type got put into the type-slot of the variable.因此,在json.Unmarshal转换为interface{}类型的变量之后,您应该检查将什么类型放入变量的类型槽中。 You can do this with an assertion and an ok boolean, or you can, if you prefer, use a type switch to decode it:您可以使用断言和ok boolean 来执行此操作,或者如果您愿意,可以使用类型开关对其进行解码:

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 ...
}

The thing is, no matter what you do in code here, you did have json.Unmarshal put something into an interface{} , and interface{} is the type of i .问题是,无论你在这里的代码中做什么,你确实json.Unmarshal将一些东西放入interface{} ,并且interface{}i的类型。 You must test at runtime what type and value pair the interface holds.您必须在运行时测试接口包含的类型和值对

Your other option is to inspect your JSON strings manually and decide what type of variable to provide to json.Unmarshal .您的另一个选择是手动检查您的 JSON 字符串并决定向json.Unmarshal提供什么类型的变量。 That gives you less code to write after the Unmarshal , but more code to write before it.这使您在Unmarshal之后编写的代码更少,但在它之前编写的代码更多。

There's a more complete example here, on the Go playground , of using type switches to inspect the result from a json.Unmarshal .这里有一个更完整的示例,在 Go 操场上,使用类型开关检查json.Unmarshal的结果。 It's deliberately incomplete but, I think, has enough input and output cases to let you work out how to handle everything, given the quote above about what json.Unmarshal writes into a variable of type interface{} .它是故意不完整的,但我认为,有足够的输入和 output 案例让你弄清楚如何处理所有事情,鉴于上面关于json.Unmarshal写入类型为interface{}的变量的引用。


1 Of course, if you assign one interface{} from some other interface{} : 1当然,如果您从其他interface{}分配一个interface{} {}:

var i1, i2 interface{}
... set i1 from some actual value ...
// more code, then:
i2 = i1

the compiler just copies both slots from i1 into i2 .编译器只是将两个插槽从i1复制到i2中。 The two-separate-slots thing becomes clearer when you do:当您这样做时,两个单独的插槽会变得更加清晰:

var f float64
... code that sets f to, say, 1.5 ...
i2 = f

for instance, as that writes float64 into the type-slot, and the value 1.5 into the value-slot.例如,将float64写入类型槽,将值1.5写入值槽。 The compiler knows that f is float64 so the type-setting just means "stick a constant in it".编译器知道ffloat64因此类型设置只是意味着“在其中插入一个常量”。 The compiler doesn't necessarily know the value of f so the value-setting is a copy of whatever the actual value is.编译器不一定知道f,因此值设置是实际值的副本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM