我正在实现一些序列化对象的方法(标准库中可能已经存在,但我想要体验)。我能够让 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 之间的差异的原因感兴趣。
慕容3067478
相关分类