使用 json 解码转换接口

Converting interfaces with json decoding

考虑以下代码:

package main

import (
    "fmt"
    "encoding/json"
)


func testNoPointer() interface{} {
    return []int{}
}

func testPointer() interface{} {
    return &[]int{}
}


func main() {
    jsonString := "[1]"
    
    noPointer := testNoPointer()
    fmt.Printf("%T\n", noPointer)
    err := json.Unmarshal([]byte(jsonString), &noPointer)
    if (err == nil) {
        fmt.Printf("%T\n", noPointer)
    }

    pointer := testPointer()
    err2 := json.Unmarshal([]byte(jsonString), &pointer)
    if (err2 == nil) {
        fmt.Printf("%T\n", pointer)
    }   
}

它输出:

[]int
[]interface {}
*[]int

为什么解组会删除值的类型信息?结果这个 decoded := noPointer.([]int) 将抛出 interface conversion: interface {} is []interface {}, not []int。是否可以将其转换为正确的类型?

您的 testNoPointer()testPointer() 函数 return interface{} 值。请理解,一旦将某些内容放入(包装)接口值中,就无法再对其进行修改。

所以在第一种情况下,您有一个包装切片的接口值。您不能将元素添加到包装的切片中,因为这需要修改切片 header(例如它的长度字段),因此在第一种情况下解组不能使用您传递的值进行解组。它必须创造新的价值。由于您要解组为 interface{} 值(noPointer 的类型是 interface{}),encoding/json 包可以自由选择它认为适合解组的任何类型,并且json.Unmarshal() 记录:

To unmarshal JSON into an interface value, Unmarshal stores one of these in the interface value:

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

您正在解组一个 JSON 数组,因此它选择 []interface{}

在您的第二个示例中,您将 *int[] 包装在接口值中。确实不能修改包装在接口中的值,但这不是必须发生的。 encoding/json包可以只修改pointed值,所以接口中的包裹指针可以保持不变,所以encoding/json包可以使用指针,因此可以保留元素类型。

请注意,在第二个示例中,包装指针不是 nil 很重要,因为如果它是 nil,则 encoding/json 包无法使用接口值包装指针:一个 nil 指针指向任何地方,因此必须分配一个新的指针。但是话又说回来,我们又到了 encoding/json 解组为 interface{} 值(pointer 的类型是 interface{}),所以 encoding/json 包将恢复为要解组的 JSON 数组创建 []interface{}

testPointer2() return 是一个 nil 指针包装时,您可以测试它:

func testPointer2() interface{} {
    var x *[]int
    return x // this will be a nil pointer
}

并对其进行测试:

pointer2 := testPointer2()
err3 := json.Unmarshal([]byte(jsonString), &pointer2)
if err3 == nil {
    fmt.Printf("%T\n", pointer2)
}

哪些输出(在 Go Playground 上尝试):

[]interface {}

我们再次“丢失”了类型信息,因为无法使用包含 nil 指针的接口值。

参见 github 上的相关问题/讨论:encoding/json: clarify what happens when unmarshaling into a non-empty interface{} #26946