Pointers Unmarshal()接受指向指针的指针
我很偶然地注意到,我可以成功地将指向结构的指针和指向结构的指针的指针传递到Pointers Unmarshal()接受指向指针的指针,pointers,go,struct,Pointers,Go,Struct,我很偶然地注意到,我可以成功地将指向结构的指针和指向结构的指针的指针传递到json.Unmarshal(),并且两者都可以正常工作: package main import ( "testing" "encoding/json" ) type Person struct { Name string Age int } func TestMarshaling(t *testing.T) { foo := &Person{Name: "bob"
json.Unmarshal()
,并且两者都可以正常工作:
package main
import (
"testing"
"encoding/json"
)
type Person struct {
Name string
Age int
}
func TestMarshaling(t *testing.T) {
foo := &Person{Name: "bob", Age: 23}
// marshal it to bytes
b, err := json.Marshal(foo)
if err != nil {
t.Error(err)
}
bar := &Person{} // pointer to new, empty struct
err = json.Unmarshal(b, bar) // unmarshal to bar, which is a *Person
if err != nil {
t.Error(err)
}
testBob(t, bar) // ok
bar = &Person{} // pointer to new, empty struct
err = json.Unmarshal(b, &bar) // wait a minute, passing in a **Person, yet it still works?
if err != nil {
t.Error(err)
}
testBob(t, bar) // ok
}
func testBob(t *testing.T, person *Person) {
if person.Name != "bob" || person.Age != 23 {
t.Error("not equal")
}
}
我真的很惊讶第二个(解组到**Person
)成功了
json.Unmarshal()
中发生了什么?它是否在找到结构之前取消对指针的引用
该文件提供:
要将JSON解组为指针,解组首先处理
JSON是JSON文本null。在这种情况下,解组集
指向nil的指针。否则,解组将JSON解组到
指针指向的值
它似乎做的不止这些。到底发生了什么事
进一步充实我的问题:它如何知道自动取消对指针的引用?文档中说它将解组“指针指向的值”。因为我的指针的值实际上是另一个指针,并且没有Name/Age字段,所以我希望它停在那里
要明确的是:我并不是说在
Unmarshal()
中有bug或错误功能;我试图满足我的惊讶,当给ptr一个ptr时,它居然能工作,并避免我使用它时的任何潜在陷阱。json包没有理由“停在指针上”,因为指针在json中毫无意义。它必须一直在树上走才能找到要写入的值。由于json包将允许将相同的值解组到类型
或*类型
,因此它应该能够将该值解组到**类型
,这也是Go中的有效类型
例如,如果使用指针定义Person
,以区分零值和零值,并且您将解组到[]*Person
的一个片段中,则json包需要遵循这些指针,并在必要时分配值。如果将个人字段定义为**字符串
,则同样适用
type Person struct {
Name **string
Age *int
}
type People []*Person
json.Unmarshal的
实现考虑了多个间接寻址。检查源代码,尤其是解码状态。间接法:
// indirect walks down v allocating pointers as needed,
// until it gets to a non-pointer.
// if it encounters an Unmarshaler, indirect stops and returns that.
// if decodingNull is true, indirect stops at the last pointer so it can be set to nil.
func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) {
// If v is a named type and is addressable,
// start with its address, so that if the type has pointer methods,
// we find them.
if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() {
v = v.Addr()
}
for {
if v.Kind() == reflect.Interface && !v.IsNil() {
e := v.Elem()
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
v = e
continue
}
}
if v.Kind() != reflect.Ptr {
break
}
//and so on
}
return nil, nil, v
在解组数组时调用相同的方法:
func (d *decodeState) array(v reflect.Value) {
u, ut, pv := d.indirect(v, false)
//...
这会让我相信围棋可以很好地处理双重间接。如果没有其他内容的话,json包源代码就是一个很好的例子
简而言之,检查值,如果解码器正在处理指针,它将使用反射计算出有多少级间接寻址,并确定目标具有/是什么类型。解码源代码的起点是:func(d*decodeState)unmarshal(v interface{})(err error){
,从这一点开始,它就相当不言自明了。正如其他答案所说,指针是遵循的
这个错误(nil指针)有点奇怪,但仔细想想是有道理的
package main
import (
"encoding/json"
"fmt"
"log"
)
type MyStruct struct {
A string `json:"a"`
}
func main() {
data := []byte(`{"a":"foo"}`)
var a *MyStruct
err := json.Unmarshal(data, a) // nil ptr
if err != nil {
log.Fatal(err)
}
fmt.Println(a)
}
但这不会出错(指针指向nil指针)
解码器必须遵循指针,并根据需要分配内部指针。这样做的结果是它将遵循多个级别的指针。除了您已经提供的答案之外,问题到底是什么?我尝试将其充实一点。:)在一个级别上,您的示例非常清楚发生了什么,谢谢。我同意Don’不要被文档中的“指针指向的值”一行误导。
package main
import (
"encoding/json"
"fmt"
"log"
)
type MyStruct struct {
A string `json:"a"`
}
func main() {
data := []byte(`{"a":"foo"}`)
var a *MyStruct
err := json.Unmarshal(data, &a) // **MyStruct, ptr to nil ptr
if err != nil {
log.Fatal(err)
}
fmt.Println(a)
}