你可以在接口上调用len()函数吗?

huangapple go评论102阅读模式
英文:

How can I call len() on an interface?

问题

我正在编写一个测试,测试一个 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]

我想在这里写什么?

英文:

I'm writing a test that a JSON list is empty.

{"matches": []}

The object has type map[string]interface{}, and I want to test that the list is empty.

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

However I'm told at compile time that this is invalid

invalid argument matches (type interface {}) for len

If I try casting to a list type:

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

I get a panic:

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

What do I want to write here?

答案1

得分: 23

在Go语言中,使用映射进行JSON解析时,到处都使用接口(interfaces)。想象一下你有以下的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] // 接口值
sv := iv.(string) // 字符串值

注意

当我说"数组"时,我指的是JSON中的数组。在Go中表示它们的数据结构被称为"切片"(slice)(Go也有数组,但它们是一个单独的概念 - 如果你习惯于C或Java等语言中的数组,Go的切片是最接近的类比)。

英文:

JSON parsing with maps in Go uses interfaces everywhere. Imagine you have the following JSON object:

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

The Go JSON library will parse the outer object as a map from keys to values, as you've seen in your code. It maps variable names as keys to the values that correspond to those variable names. However, since it has no way of knowing ahead of time what those values are, the value type of the map is simply interface{}. So let's say you know there's a key called "stuff", and you know that its value is an array. You could do:

arr := myMap["stuff"]

And you know that it's an array type, so you can actually instead do:

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

the problem here is that while you're right that it's an array, and the JSON library knows this, it has no way of knowing that every element will be of type string, so there's no way for it to decide that the array type should actually be []string. Imagine if you had done this instead:

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

Well "stuff" can't now be an array of strings because one of the elements isn't a string. In fact, it can't be an array of anything - there's no single type that would satisfy the types of all of the elements. So that's why the Go JSON library has no choice but to leave it as []interface{}. Luckily, since all you want is the length, you're already done. You can just do:

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

Now that's all fine and good, but let's say that down the road you want to actually look at one of the elements. You could now take out an element and, knowing that it's a string, do:

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

NOTE

When I say "array," I mean array in the JSON sense - these are JSON arrays. The data structure that represents them in Go is called a "slice" (Go has arrays too, but they're a separate thing - if you're used to arrays in languages like C or Java, Go slices are the closest analogue).

答案2

得分: 1

处理JSON时,可以为arrayobject添加类型声明,然后根据需要添加方法来帮助进行转换:

package main
import "encoding/json"

type (
   array []interface{}
   object map[string]interface{}
)

func (o object) a(s string) array {
   return o
展开收缩
.([]interface{}) } func main() { data := []byte(`{"matches": []}`) var response object json.Unmarshal(data, &response) matches := response.a("matches") mLen := len(matches) println(mLen == 0) }

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

英文:

When dealing with JSON, you can add type declarations for array and object, then add methods as needed to help with conversion:

package main
import "encoding/json"

type (
   array []interface{}
   object map[string]interface{}
)

func (o object) a(s string) array {
   return o
展开收缩
.([]interface{}) } func main() { data := []byte(`{"matches": []}`) var response object json.Unmarshal(data, &response) matches := response.a("matches") mLen := len(matches) println(mLen == 0) }

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

huangapple
  • 本文由 发表于 2014年1月12日 09:59:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/21070387.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定