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)
}