为了账号安全,请及时绑定邮箱和手机立即绑定

json.Unmarshal() 接受一个指向指针的指针

json.Unmarshal() 接受一个指向指针的指针

Go
慕神8447489 2021-12-20 09:45:06
我很偶然地注意到,我可以成功地将指向结构的指针和指向指向结构的指针传递给json.Unmarshal(),并且两者都可以正常工作:package mainimport (    "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 解组为指针,Unmarshal 首先处理 JSON 为 JSON 文本 null 的情况。在这种情况下,Unmarshal 将指针设置为 nil。否则,Unmarshal 将 JSON 解组为指针指向的值它似乎做得更多。到底是怎么回事?更多地充实我的问题:它如何知道自动取消对我的指针的引用?文档说它将解组“到指针指向的值中”。由于我的指针的值实际上是另一个指针,并且没有 Name/Age 字段,因此我希望它停在那里。需要明确的是:我并不是说 ; 中存在错误或错误功能Unmarshal()。我试图满足我的惊讶,即在给定 ptr-to-ptr 时它仍然有效,并避免在我使用它时出现任何潜在的陷阱。
查看完整描述

3 回答

?
慕盖茨4494581

TA贡献1850条经验 获得超11个赞

json 包没有理由“停在一个指针”,因为指针在 json 中没有任何意义。它必须继续遍历树才能找到要写入的值。由于 json 包将允许将相同的值解组为Typeor *Type,因此它应该能够将其解组为**Type,这也是 Go 中的有效类型。


例如,如果Person使用指针定义来区分 nil 和零值,并且您正在解组为 的切片[]*Person,则 json 包需要遵循这些指针,并在必要时分配值。如果 Person 中的字段被定义为**string.


type Person struct {

    Name **string

    Age  *int

}


type People []*Person

http://play.golang.org/p/vLq0nJPG5M


查看完整回答
反对 回复 2021-12-20
?
繁星淼淼

TA贡献1775条经验 获得超11个赞

该json.Unmarshal实现考虑了多个间接性。检查来源here,特别是decodeState.indirect方法:


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

    //...

那会让我相信 go 可以很好地处理双重间接。如果不出意外,json 包源代码是反射包全部内容的一个很好的例子。


简而言之,检查值,如果解码器正在处理指针,它将使用反射来计算有多少间接级别,并确定目标具有/是什么类型。从解码源开始的地方是这样的:func (d *decodeState) unmarshal(v interface{}) (err error) {从那一点开始,它是不言自明的。


查看完整回答
反对 回复 2021-12-20
?
慕妹3146593

TA贡献1820条经验 获得超9个赞

正如其他答案所说,遵循指示。


这个错误(零指针)有点奇怪,但当你考虑它时是有道理的。


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 指针的指针)。


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)

}

https://play.golang.org/p/eI8jqWZOmGW


查看完整回答
反对 回复 2021-12-20
  • 3 回答
  • 0 关注
  • 223 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信