[英]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).我有一个变量需要是
string
或map[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-CVk7bdb9见https://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/hCl8eBcKSqE见https://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{}
类型,但它拥有其他类型的值。
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".编译器知道
f
是float64
因此类型设置只是意味着“在其中插入一个常量”。 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.