如何在界面上调用len()?

时间:2014-01-12 01:59:06

标签: go

我正在编写一个JSON列表为空的测试。

{"matches": []}

对象的类型为map[string]interface{},我想测试列表是否为空。

var matches := response["matches"]
if len(matches) != 0 {
    t.Errorf("Non-empty match list!")
}

但是我在编译时被告知这是无效的

invalid argument matches (type interface {}) for len

如果我尝试转换为列表类型:

matches := response["matches"].([]string)

我感到恐慌:

panic: interface conversion: interface is []interface {}, not []string [recovered]

我想在这里写些什么?

2 个答案:

答案 0 :(得分:14)

使用Go中的地图进行JSON解析时使用无处不在的接口。想象一下,你有以下JSON对象:

{
    "stuff" : [
        "stuff1",
        "stuff2",
        "stuff3",
    ]
}

Go JSON库将外部对象解析为从键到值的映射,正如您在代码中看到的那样。它将变量名称映射为与这些变量名称对应的值的键。但是,由于无法提前知道这些值,因此地图的值类型只是interface{}。所以,假设您知道有一个名为"stuff"的密钥,您知道它的值是一个数组。你可以这样做:

arr := myMap["stuff"]

而且你知道它是一个数组类型,所以你实际上可以这样做:

arr := myMap["stuff"].([]interface{})

这里的问题是,虽然你是对的,它是一个数组,并且JSON库知道这一点,但它无法知道每个元素都是string类型,所以没有办法确定数组类型实际上应该是[]string。想象一下,如果你这样做了:

{
    "stuff" : [
        "stuff1",
        "stuff2",
        3
    ]
}

"stuff"现在不能是字符串数组,因为其中一个元素不是字符串。实际上,它不能是任何的数组 - 没有一种类型可以满足所有元素的类型。这就是为什么Go JSON库别无选择,只能将其保留为[]interface{}。幸运的是,既然你想要的只是长度,你已经完成了。你可以这样做:

arr := myMap["stuff"].([]interface{})
l := len(arr)

现在这一切都很好,但是让我们说你想要实际看到其中一个元素。您现在可以取出一个元素,并且知道它是一个字符串,执行:

arr := myMap["stuff"].([]interface{})
iv := arr[0] // interface value
sv := iv.(string) // string value

注意

当我说“数组”时,我指的是JSON意义上的数组 - 这些是JSON数组。在Go中表示它们的数据结构称为“切片”(Go也有数组,但它们是一个单独的东西 - 如果您习惯使用C或Java等语言中的数组,则Go切片是最接近的模拟)。

答案 1 :(得分:0)

在处理 JSON 时,我喜欢为 Map 和 Slice 添加类型声明,然后 我可以根据需要添加方法来帮助转换:

package main
import "encoding/json"
type Map map[string]interface{}
type Slice []interface{}

func (m Map) A(s string) Slice {
   return m[s].([]interface{})
}

func main() {
   y := []byte(`{"matches": []}`)
   response := Map{}
   json.Unmarshal(y, &response)
   matches := response.A("matches")
   n := len(matches)
   println(n == 0)
}

https://golang.org/ref/spec#Type_declarations