基于对象何时转换为指针(有时是指针,有时是映射)更改解组功能

我正在实现一些序列化对象的方法(标准库中可能已经存在,但我想要体验)。我能够让 json.Marshal 和 json.Unmarshal 正确地将我的数据转换为字符串,然后读回一个对象,但我注意到一些我想更好地理解的有趣行为。


为了测试 json 包的功能,我创建了一个测试结构:


type Test struct{

    T int

    S string

    F float32

}

跑步


o := Test{32, "struct", 12.07}

b, e := json.Marshal(o)

fmt.Println(string(b), e)

印刷


{"T":32,"S":"struct","F":12.07} <nil>

这是我所期望的。但是,在解组时我能够得到两个不同的结果,具体取决于我何时将对象转换为指针:


// Test 1

var o2 interface{} = &o

e = json.Unmarshal(b, o2)

fmt.Println(o2, e)

印刷


// Output 1

&{32 struct 12.07} <nil>

同时将 o2 定义为值而不是指针,然后使用 &o2 调用 Unmarshal,即。


// Test 2

var o2 interface{} = o

e = json.Unmarshal(b, &o2)

fmt.Println(o2, e)

印刷


// Output 2

map[T:32 S:struct F:12.07] <nil>

有趣的是,所有四个


// Test 3

var o2 Test = o

e = json.Unmarshal(b, &o2)

fmt.Println(o2, e)

,


// Test 4

var o2 *Test = &o

e = json.Unmarshal(b, &o2)

fmt.Println(*o2, e)

,


// Test 5

var o2 *Test = &o

e = json.Unmarshal(b, o2)

fmt.Println(o2, e)

, 和


// Test 6

var o2 *Test = &o

e = json.Unmarshal(b, &o2)

fmt.Println(o2, e)

打印以下内容之一:


// Output 3, 4

{32 struct 12.07} <nil>


// Output 5, 6

&{32 struct 12.07} <nil>

测试 1 和 2 让我相信,当将某些东西分配给“超类”时,一些运行时信息会丢失......似乎将指向我的结构的指针分配给 o2 为 Unmarshal 函数提供了更多关于它应该读取的内容的类型数据而不是将我的结构分配给 o2,然后将指向 o2 的指针传递给 Unmarshal。这有点道理;在编译时,测试 1 将 interface{} 传递给 Unmarshal,而 Test 2 将指针传递给 interface{}。即便如此,我还是认为参数的运行时类型会是相同的(*测试)。有人可以解释为什么 Go 会这样工作吗?


支持我的结论的事实是,将 o2 声明为 Test 或 *Test 允许 Unmarshal(因此接收 *Test 作为其参数)始终将我的数据读取为结构而不是映射。


奇怪的是,测试 4 和测试 6 表明将指针传递给指向我的结构的指针是完全可以接受的,并且 Unmarshal 正确(?)设置了结构的值。我本以为这些测试会出现运行时错误,因为 Unmarshal 应该尝试取消引用双指针并将结果指针设置为它正在读取的序列化结构。Automatic multi-pointer dereferencing 可能只是这个功能的一个特性,但在官方文档中并没有提到。我不太关心后面的这些测试。我主要只对导致测试 1 和测试 2 之间的差异的原因感兴趣。



小唯快跑啊
浏览 106回答 1
1回答

慕容3067478

unmarshal 函数遍历指针和包含指针值的接口以找到目标值。接口中的非指针值将被忽略,因为这些值不可寻址。(此描述省略了对问题不重要的细节)。如果目标是 aninterface{}并且 JSON 是对象,则 JSON 被解组为 amap[string]interface{}并将该值存储在接口中。测试 1:解组的参数是*Test。unmarshal 函数遍历指针并解码为Test结构。测试 2:unmarshal 的参数是指向interface{}包含Test. unmarshal 函数遍历指针以获取interface{}. Test中的值被interface{}忽略,因为它不可寻址。因为目标是 aninterface{}而 JSON 是对象,所以 JSON 被解码为map[string]interface{}.3、4、5 和 6 中的代码无法编译。假设删除了{}以下内容,我将继续Test。var o2 Test = o // 3var o2 *Test = &o // 4var o2 *Test = &o // 5var o2 *Test = &o // 6测试 3 和 5:unmarshal 的参数是一个*Test. 这与#1 相同。测试 4 和 6:参数是一个**Test. unmarshal 函数遍历指针并解码到Test结构。因为 Go 没有任何类似“超类”的东西,所以这不是问题。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go